資料結構學習——skiplist跳錶
目錄
1.skiplist簡介
Skiplist是一個用於有序元素序列快速搜尋的資料結構,由美國電腦科學家William Pugh發明於1989年。他在論文《Skip lists: a probabilistic alternative to balanced trees》中詳細介紹了跳錶的資料結構和插入刪除等操作。論文是這麼介紹跳錶的:
- Skip lists are a data structure that can be used in place of balanced trees.
- Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.
也就是說,Skip list是一個“概率型”的資料結構,可以在很多應用場景中替代平衡樹。Skip list演算法與平衡樹相比,有相似的漸進期望時間邊界,但是它更簡單,更快,使用更少的空間。參考演算法導論,跳錶插入、刪除、查詢的複雜度均為O(logN)。
選擇跳錶的原因:
- 目前經常使用的平衡資料結構有:B樹,紅黑樹,AVL樹,Splay Tree, Treep等,但是實現細節過於複雜。
- 相比之下,跳錶的原理相當簡單,只要你能熟練操作連結串列,就能輕鬆實現一個 SkipList。目前開源軟體 Redis 和 LevelDB 都有用到它。
2.skiplist核心思想
先從連結串列開始,如果是一個單鏈表,那麼我們知道在連結串列中查詢一個元素I的話,需要將整個連結串列遍歷一次,時間複雜度為O(n)。
如果是說連結串列是排序的,並且結點中還儲存了指向後面第二個結點的指標的話,那麼在查詢一個結點時,僅僅需要遍歷N/2個結點即可。因此查詢的時間複雜度為O(n/2)。
其實,上面基本上就是跳躍表的思想,每一個結點不單單隻包含指向下一個結點的指標,可能包含很多個指向後續結點的指標,這樣就可以跳過一些不必要的結點,從而加快查詢、刪除等操作。對於一個連結串列內每一個結點包含多少個指向後續元素的指標,這個過程是通過一個隨機函式生成器得到,這樣子就構成了一個跳躍表。這就是為什麼論文“Skip Lists : A Probabilistic Alternative to Balanced Trees ”中有“概率”的原因了,就是通過隨機生成一個結點中指向後續結點的指標數目。隨機生成的跳躍表可能如下圖所示。
如果一個結點存在k個指向後續元素指標的話,那麼稱該結點是一個k層結點。一個跳錶的層MaxLevel義為跳錶中所有結點中最大的層數。跳錶的所有操作均從上向下逐層進行,越上層一次next操作的跨度越大。
3.skiplist原理
skiplist的結構定義如下:
- 由多層結構組成;
- 每一層都是一個有序的連結串列;
- 最底層(Level 1)的連結串列包含所有元素;
- 如果一個元素出現在 Level i 的連結串列中,則它在 Level i 之下的連結串列也都會出現;
- 每個節點包含兩個指標,一個指向同一連結串列中的下一個元素,一個指向下面一層的元素。
為了便於描述,將跳錶結構繪畫成如下形式。
3.1 跳錶的查詢
例子:查詢元素 117,會經歷如下幾個步驟:
- 比較 21, 比 21 大,往後面找
- 比較 37, 比 37大,比連結串列最大值小,從 37 的下面一層開始找
- 比較 71, 比 71 大,比連結串列最大值小,從 71 的下面一層開始找
- 比較 85, 比 85 大,從後面找
- 比較 117, 等於 117, 找到了節點。
3.2 跳錶的插入
1)K小於連結串列的層數
先確定該元素要佔據的層數 K(採用丟硬幣的方式,這完全是隨機的),然後在 Level 1 ... Level K 各個層的連結串列都插入元素。
例子:插入 119, K = 2
2)K大於連結串列的層數
如果 K 大於連結串列的層數,則要新增新的層。
例子:插入 119, K = 4
插入元素的時候,元素所佔有的層數完全是隨機的,通過某種隨機演算法產生。
3.3跳錶的刪除
在各個層中找到包含 x 的節點,使用標準的 delete from list 方法刪除該節點。
例子:刪除 71
4.skiplist簡單實現
參考連結:https://blog.csdn.net/caoshangpa/article/details/78862339