1. 程式人生 > >數據結構與算法之美專欄學習筆記-散列表(下)

數據結構與算法之美專欄學習筆記-散列表(下)

檢查 速查 刪除 core 筆記 意思 前驅 表示 就是

散列表和鏈表組合使用

LRU緩存淘汰算法

借助散列表,我們可以把LRU緩存淘汰算法的時間復雜度降為O(1)。

一個緩沖cache系統主要包含以下操作

  • 往緩存中添加一個數據;
  • 從緩存中刪除一個數據;
  • 在緩存中查找一個數據。

單純采用鏈表,時間復雜度只能是O(n)。

將散列表和雙向鏈表結合,就可以降為O(1),其結構如下圖所示:

技術分享圖片

其中,我們使用雙向鏈表存儲數據,data存儲數據,prev前驅指針,next後繼指針。

此外,新增加了hnext指針,這個指針就是鏈表法散列表中的拉鏈的後繼指針。

如何做到O(1)

查找

因為是散列表所以查找一個數據的操作時間復雜度就接近於O(1)。

刪除

刪除一個數據,我們借助散列表再O(1)的時間復雜度裏找到該結點,

而雙向鏈表有前驅指針,可以直接刪除該節點,時間復雜度為O(1)。

添加

添加一個數據比較復雜,首先要看其是否已經在緩存中,

如果在就將其移動到雙向鏈表的尾部,如果不在就檢查緩存滿了沒,

滿了就刪除雙向鏈表的頭結點,再將數據放到雙向鏈表的尾部,

如果沒有滿就直接將數據放大雙向鏈表的尾部。

以上操作中,設計查找的操作是散列表完成的,刪除節點、插入節點是雙向鏈表完成的,所以時間復雜度是O(1)。

Redis有序集合

在有序集合中,每個成員對象有兩個重要的屬性,鍵key和分值score。

我們不僅需要key來查找數據,還會需要用score查找數據。

細化一下Redis有序集合的操作:

  • 添加一個成員對象;
  • 按照鍵值來刪除一個成員對象;
  • 按照鍵值來查找一個成員對象;
  • 按照分值區間查找數據,比如查找積分在[100, 356] 之間的成員對象;
  • 按照分值從小到大排序成員變量;

如果只按照分支將成員對象組織成跳表的結構,那麽按照鍵值刪除、查詢對象就會很慢。

我們可以按照鍵值構建一個散列表,這樣按照key來刪除、查找一個對象的時間復雜度就都變成了O(1)。

Java中的LinkedHashMap

Jva中的LinkedHashMap中的Linked並不是鏈表法表示散列表的意思,而是雙向鏈表和散列表結合。

LinkedHashMap本身就是一個支持LRU緩存淘汰策略的緩存系統,其數據的存取移動刪除規則和LRU一樣。

思考

今天講的幾個散列表和鏈表結合使用的例子裏,我們用的都是雙向鏈表。如果把雙向鏈表改成單鏈表,還能否正常工作呢?為什麽呢?

假設獵聘網有 10 萬名獵頭,每個獵頭都可以通過做任務(比如發布職位)來積累積分,然後通過積分來下載簡歷。

假設你是獵聘網的一名工程師,如何在內存中存儲這 10 萬個獵頭 ID 和積分信息,讓它能夠支持這樣幾個操作:

  • 根據獵頭的 ID 快速查找、刪除、更新這個獵頭的積分信息;
  • 查找積分在某個區間的獵頭 ID 列表;
  • 查找按照積分從小到大排名在第 x 位到第 y 位之間的獵頭 ...

數據結構與算法之美專欄學習筆記-散列表(下)