數據結構 | SkipList(跳表)


寫在前面

該文並不是跳表的入門文章,而是致力於以簡潔精煉的語言來描述 SkipList,來彌補上次面試時被問到跳表結果腦中只有圖片沒有文字的尷尬場景。。。



SkipList(跳表)

SkipList 是一種查找結構

結構

它的結構是一個有序鏈表,但是該鏈表的節點的具有多個指針,且不止指向下一個節點。在該鏈表上,每 N 個節點還會擁有一個指向后第 N 個節點的指針,N 值通常為 2 的冪。一個節點上可能具有多個 N 值指針,指針按照 N 值大小從高到低排序,分為多個"層"


查找

當要查找的時候:

  1. 頭節點具有所有的 N 值的指針,頭節點為當前節點
  2. 取出當前節點具有的最高的 N 值的指針
  3. 觀察該指針指向的節點的值
  4. 如果等於目標值,說明節點找到,退出
  5. 如果大於目標值,將當前高度減一
    1. 若此時高度為零,說明值不存在,退出
    2. 否則,返回到 第 3 步
  6. 如果小於目標值,前進到該節點,返回到 第 2 步
  7. 當來到尾部時,代表值不存在,退出

在查找的時候,由於 N 值為 2 的冪,故每一次 高度減一 ,需要查找的節點就會減少一半,所以時間復雜度為 \(O(logN)\)

但如果這樣,由於插入和刪除時需要維護 N 值的正確性,時間復雜度可能退化為 \(O(N)\) 。所以改進為了插入時高度隨機取值。

且為了保證層數越高越稀疏,該隨機值不會隨機分布,其計算過程如下:

  1. 起始高度為 1
  2. 下一層被創建的概率為 \(P\)
  3. 若未到達高度上限,且創建成功,則回到第二步
  4. 此時的高度值則為要插入節點的當前高度

插入

對於插入來講,由於我們使用了隨機高度的做法,所以不需要維護 N 值的正確性,故變得十分簡單。

只需要先進行一次查找的過程,同時記錄發生了高度下降的節點,在查找完成后根據高度,來從記錄中更改引用關系。

時間復雜度為 \(O(logN)\)


刪除

刪除則也與插入同理,只不過更改引用關系時執行的是將前置節點與后置節點連接的操作。

但是需要這需要對所有高度進行查找,所以節點最好選用雙向鏈表,可以減少復雜度。

時間復雜度為 \(O(logN)\)


與紅黑樹的對比

跳表的時間復雜度與紅黑樹相同,且都是用於在內存中的一種查找結構。

當然也有不同的地方,Redis 的開發者還給出了使用跳表的理由

There are a few reasons:

  1. They are not very memory intensive. It's up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.

  2. A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.

  3. They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM