Redis資料結構 鍊錶

2021-10-10 00:18:40 字數 3138 閱讀 1647

文章導航-readmeredis鍊錶為雙向無環鏈表!

**redis之資料結構篇——簡單動態字串sds提到redis使用了簡單動態字串,鍊錶,字典(雜湊表),跳躍表,整數集合,壓縮列表這些資料結構來操作記憶體,並且簡單介紹了redis簡單動態字串。本篇文章我們繼續來分析鍊錶。

鍊錶是一種非常常見的資料結構,在redis中使用非常廣泛,列表物件的底層實現之一就是鍊錶。其它如慢查詢,發布訂閱,監視器等功能也用到了鍊錶。

1.1 陣列與鍊錶

陣列需要一塊連續的記憶體來儲存,這個特性有利也有弊。好處是其支援根據索引下標"隨機訪問"(時間複雜度為o(1)),但是其插入與刪除操作為了保證在記憶體中的連續性將會變得非常低效(時間複雜度為o(n)),並且其一經宣告就要占用整塊連續記憶體空間,如果宣告過大,系統可能記憶體不足,宣告過小又可能導致不夠用,而當陣列的空間不足的時候需要對其進行擴容(申請乙個更大的空間,將原陣列拷貝過去)。

而鍊錶恰恰相反,其不需要一塊連續的記憶體空間,其通過"指標"將一組零散的記憶體連線起來使用。其優點在於本身沒有大小限制,天然支援擴容,插入刪除操作高效(時間複雜度為o(1)),但缺點是隨機訪問低效(時間複雜度為o(n))。並且由於需要額外的空間儲存指標。

鍊錶的實現方式有很多種,常見的主要有三個,單向鍊錶、雙向鍊錶、迴圈鍊錶。

1.2 單鏈表

單鏈表中每個節點除了包含資料之外還包含乙個指標,叫後繼指標,因此需要額外的空間來儲存後繼節點的位址。有兩個特殊的節點,頭結點和尾節點,其中頭節點用來記錄鍊錶的基位址,有了它就可以遍歷整個鍊錶,尾節點的後繼指標不是指向下乙個節點,而是指向乙個空位址null表示這是鍊錶上最後乙個節點。與陣列一樣,單鏈表也支援資料的查詢、插入和刪除操作,其中插入和刪除操作只需要考慮相鄰節點指標的變化,因此為常數級時間複雜度o(1)。要想隨機訪問第 k 個元素,就沒有陣列那麼高效了。因為鍊錶中的資料並非連續儲存的,所以無法像陣列那樣,根據首位址和下標,通過定址公式就能直接計算出對應的記憶體位址,而是需要根據指標乙個結點乙個結點地依次遍歷,直到找到相應的結點,因此時間複雜度為o(n)。

1.3 雙向鍊錶

雙向鍊錶和單鏈表不同的是多了乙個前驅指標,雙向鍊錶需要額外的兩個空間來儲存後繼結點和前驅結點的位址。因此儲存同樣多的資料,雙向鍊錶占用比單鏈表更多的空間。但其優點在於支援雙向遍歷,體現在以下兩個方面。

1.4 迴圈鍊錶

顧名思義。迴圈鍊錶與單、雙鏈表不同的是其呈環狀,單迴圈鍊錶中其尾節點並非指向null而是指向頭結點。雙迴圈鍊錶中其頭節點的前驅指標指向尾節點,尾節點的後繼指標指向頭結點。迴圈鍊錶的優勢在於鏈尾到鏈頭,鏈頭到鏈尾比較方便適合處理的資料具有環型結構特點。

2.1 雙向無環鏈表

redis鍊錶使用雙向無環鏈表。

如圖所示,redis使用乙個listnode結構來表示。

typedef struct listnode

listnode;

2.2 list結構

同時redis為了方便的操作鍊錶,提供了乙個list結構來持有鍊錶。如下圖所示

typedef struct listlist;
redis鍊錶結構其主要特性如下:

2.3 雙向無環鏈表在redis中的使用

鍊錶在redis中的應用非常廣泛,列表物件的底層實現之一就是鍊錶。此外如發布訂閱、慢查詢、監視器等功能也用到了鍊錶。我們現在簡單想一想redis為什麼要使用雙向無環鏈表這種資料結構,而不是使用陣列、單向鍊錶等。既然列表物件的底層實現之一是鍊錶,那麼我們通過乙個**來分析列表物件的常用操作命令。如果分別使用陣列、單鏈表和雙向鍊錶實現列表物件的時間複雜度對照如下:

操作\時間複雜度

陣列單鏈表

雙向鍊錶

rpush(從右邊新增元素)

o(1)

o(1)

o(1)

lpush(從左邊新增元素)

0(n)

o(1)

o(1)

lpop (從右邊刪除元素)

o(1)

o(1)

o(1)

rpop (從左邊刪除元素)

o(n)

o(1)

o(1)

lindex(獲取指定索引下標的元素)

o(1)

o(n)

o(n)

len (獲取長度)

o(n)

o(n)

o(1)

linsert(向某個元素前或後插入元素)

o(n)

o(n)

o(1)

lrem (刪除指定元素)

o(n)

o(n)

o(n)

lset (修改指定索引下標元素)

o(n)

o(n)

o(n)

我們可以看到在列表物件常用的操作中雙向鍊錶的優勢所在。但雙向鍊錶因為使用兩個額外的空間儲存前驅和後繼指標,因此在資料量較小的情況下會造成空間上的浪費(因為資料量小的時候速度上的差別不大,但空間上的差別很大)。這是乙個時間換空間還是空間換時間的思想問題,redis在列表物件中小資料量的時候使用壓縮列表作為底層實現,而大資料量的時候才會使用雙向無環鏈表。(關於列表物件後續會有文章繼續介紹可訪問我的個人部落格持續關注www.kxamm.com)

《redis設計與實現》

《redis開發與運維》

《redis官方文件》

Redis資料結構之鍊錶

鍊錶基礎知識 鍊錶是一種物理儲存單元上非連續 非順序的儲存結構,資料元素的邏輯順序通過鍊錶中的指標鏈結次序來實現。其內部是由一系列的資料節點組成,資料節點可以動態分配。鍊錶與陣列的比較 陣列可以隨機訪問,鍊錶只能順序訪問。鍊錶容量可以動態擴充陣列不可以,鍊錶新增資料元素,不需要資料的移動。陣列新增元...

Redis資料結構 二 鍊錶

鍊錶提供了高效的節點重排能力,以及順序性的節點訪問方式,並且可以通過增刪節點來靈活的調整鍊錶的長度 列表鍵的底層實現之一就是鍊錶.當乙個列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字串時,redis就會使用鍊錶作為列表鍵的底層實現 除了列表鍵之外,發布與訂閱,慢查詢,監視器等功能...

Redis資料結構 鍊錶 linkedlist

鍊錶簡介 因為c語言沒有內建鍊錶這種資料結構,所以redis構建了自己的鍊錶實現。列表鍵的底層實現之一就是鍊錶。當乙個列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字串時,redis就會使用鍊錶作為列表鍵的底層實現。1 鍊錶實現 鍊錶結點資料結構 雖然使用多個listnode結構就...