原始碼筆記4 跳躍表skiplist

2021-10-25 22:07:46 字數 3886 閱讀 4196

//跳躍表節點

/* zsets use a specialized version of skiplists */

typedef

struct zskiplistnode level;

// 該節點擁有的層級陣列 (柔性陣列)

} zskiplistnode;

// 跳躍表結構

typedef

struct zskiplist zskiplist;

#define zskiplist_maxlevel 32 

/* should be enough for 2^32 elements */

#define zskiplist_p 0.25

/* skiplist p = 1/4 */

可以先看下有序集合的結構,其中就包含跳躍表的實現。後面再單獨分析有序集合zset。

// 有序集合

typedef

struct zset zset;

先根據個人理解,畫一張跳躍表的圖,為了更好的理解下面的內容。

跳躍表除去「跳躍」的特性本身是個鍊錶,「跳躍」由level這個層級化的資訊中的指標帶來的。

程式設計上,總是從高層級開始訪問,相當於大範圍的查詢,隨著查詢範圍縮小,慢慢的降低層級,有一種跳著查的感覺。

zslcreatenode建立跳躍表節點

// 層數level 分數score 物件是obj

zskiplistnode *

zslcreatenode

(int level,

double score, robj *obj)

zslcreate建立乙個跳躍表

zskiplist *

zslcreate

(void

)// 頭結點的後退指標null

zsl->header->backward =

null

;// 尾結點

zsl->tail =

null

;return zsl;

}

zslrandomlevel隨機獲取乙個level,後面再細講。

zslinsert插入redis物件

zskiplistnode *

zslinsert

(zskiplist *zsl,

double score, robj *obj)

// 記錄將要和新節點相連線的節點

update[i]

= x;

}// 新節點的層數,通過隨機值獲取

level =

zslrandomlevel()

;// 如果新節點的層數大於當前跳躍表的最高層數

if(level > zsl->level)

// 更新表中節點最大層數

zsl->level = level;

}// 建立新節點

x =zslcreatenode

(level,score,obj)

;// 將前面記錄的指標指向新節點,並做相應的設定

for(i =

0; i < level; i++

)// 未接觸的節點的 span 值也需要增一,這些節點直接從表頭指向新節點

for(i = level; i < zsl->level; i++

)// 設定新節點的後退指標

x->backward =

(update[0]

== zsl->header)

?null

: update[0]

;if(x->level[0]

.forward)

x->level[0]

.forward->backward = x;

else

zsl->tail = x;

// 跳躍表的節點計數增一

zsl->length++

;return x;

}

zsldeletenode刪除指定節點

zsldelete刪除redis物件(先查詢,再呼叫zsldeletenode)

zslfreenode釋放指定的跳躍表節點

zslfree釋放指定的跳躍表

// 其中遍歷 所有節點的**如下

// 因為物件都在第一層,只要一直遍歷前進指標即可

while

(node)

先看乙個簡單的有序鍊錶:

在這樣乙個鍊錶中,如果要查詢某個資料,就需要從頭開始逐個比較,直到找到指定的節點。時間複雜度是o(n)。

現在假設每兩個節點,增加乙個指標。在這樣乙個鍊錶中,如果要查詢某個資料,同樣從頭開始逐個比較,但是它一次會跳過兩個節點,相比上乙個鍊錶,整體時間複雜度變成了o(n/2)。

按照這個思路,每三個節點再增加乙個指標,讓跳躍距離更大,這樣就擁有了更快的查詢能力。可以想象,當鍊表的長度比較大的時候,這樣多個層次的鍊錶進行「跳躍式」的查詢,能跳過很多下層的節點,大大提高查詢速度。

記下來思考,我們該怎麼去構建這個跳躍鍊錶,真的按照上面的想法,每幾個節點乙個迴圈這樣有序的高度變化嗎?

試想一下如果這麼做,新插入乙個節點,該怎麼維持原先的迴圈有序呢,需要調整新節點以及後面所有的節點。刪除乙個節點同理。那麼維護這樣乙個鍊錶,就變的更加麻煩。實際上,redis為了避免這個問題,採取的隨機高度的做法,每個節點的高度都是隨機性的,在整體上實現每個層次的數量級分化。

上面在讀redis的跳躍表時,有乙個函式是zslrandomlevel。具體**如下:

// zskiplist_maxlevel=32

// zskiplist_p=0.25

intzslrandomlevel

(void

)

這個函式,在每個新增節點時都會呼叫,也就是每個節點都會隨機出乙個level高度。

怎麼理解這件事。

函式中有乙個迴圈,條件是隨機數小於1/4,高度就增加1。從巨集觀角度上來看,假設有1000個節點,那麼就大概會有250個節點高度大於1,62個節點高度大於2,16個節點高度大於3… 大致就是每一層之上的節點數大約是這一層節點數的

1/4,這樣整體保證了這個跳躍表不會過於臃腫也不會失去跳躍性。

redis取1/4,這個數字不是固定的,在自己的設計中,也可以更改,是乙個彈性設計。

上圖是乙個redis中可能生成的跳躍表示例,現在來查詢9,需要的步驟是1->4->6->9,只需要4次就找到對應目標,查詢示例如下

redis zskiplist 跳躍表原始碼學習

typedef struct zskiplistnode level index越大表示層級越高 zskiplistnode typedef struct zskiplist zskiplist typedef struct zset zset 命令 zadd key score value zsk...

redis原始碼解析 跳躍表

定義 跳躍表是一種有序資料結構,它通過在每個節點中維護多個指向其他節點的指標,從而達到快速訪問節點的目的。跳躍表支援平均o logn 最壞o n 複雜度的節點查詢,大部分情況下,跳躍表的效率可以和平衡樹相媲美,並且因為跳躍表的實現比平衡樹要來得簡單,所以有不少程式都使用跳躍表來替代平衡樹。從圖中可以...

redis原始碼學習之跳躍表

跳躍表對於我來說是乙個比較陌生的資料結構,因此花了一上午的時間先看了一蛤mit的公開課。網易雲課堂 mit跳躍表 什麼是跳躍表,有乙個很簡單的例子,有些地方的火車站跟高鐵站是同乙個站,有的地方只有火車站 假設現在的線路是a b c d e。其中a和c剛剛說的高鐵和火車站在一塊,其他的只有火車站,考慮...