最近看了一種數據結構叫做skipList,redis和levelDB都是用了它。Skip List是在有序鏈表的基礎上進行了擴展,解決了有序鏈表結構查找特定值困難的問題,查找特定值的時間復雜度為O(logn),他是一種可以代替平衡樹的數據結構。
下面是skipList的一個介紹,轉載來的,源地址:http://kenby.iteye.com/blog/1187303,為防止源地址丟失,故拷貝一份放在這里,望作者原諒。
———————————————轉載開始—————————————————
為什么選擇跳表
目前經常使用的平衡數據結構有:B樹,紅黑樹,AVL樹,Splay Tree, Treep等。
想象一下,給你一張草稿紙,一只筆,一個編輯器,你能立即實現一顆紅黑樹,或者AVL樹
出來嗎? 很難吧,這需要時間,要考慮很多細節,要參考一堆算法與數據結構之類的樹,
還要參考網上的代碼,相當麻煩。
用跳表吧,跳表是一種隨機化的數據結構,目前開源軟件 Redis 和 LevelDB 都有用到它,
它的效率和紅黑樹以及 AVL 樹不相上下,但跳表的原理相當簡單,只要你能熟練操作鏈表,
就能輕松實現一個 SkipList。
有序表的搜索
考慮一個有序表:
從該有序表中搜索元素 < 23, 43, 59 > ,需要比較的次數分別為 < 2, 4, 6 >,總共比較的次數
為 2 + 4 + 6 = 12 次。有沒有優化的算法嗎? 鏈表是有序的,但不能使用二分查找。類似二叉
搜索樹,我們把一些節點提取出來,作為索引。得到如下結構:
這里我們把 < 14, 34, 50, 72 > 提取出來作為一級索引,這樣搜索的時候就可以減少比較次數了。
我們還可以再從一級索引提取一些元素出來,作為二級索引,變成如下結構:
這里元素不多,體現不出優勢,如果元素足夠多,這種索引結構就能體現出優勢來了。
跳表
下面的結構是就是跳表:
其中 -1 表示 INT_MIN, 鏈表的最小值,1 表示 INT_MAX,鏈表的最大值。
跳表具有如下性質:
(1) 由很多層結構組成
(2) 每一層都是一個有序的鏈表
(3) 最底層(Level 1)的鏈表包含所有元素
(4) 如果一個元素出現在 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會出現。
(5) 每個節點包含兩個指針,一個指向同一鏈表中的下一個元素,一個指向下面一層的元素。
跳表的搜索
例子:查找元素 117
(1) 比較 21, 比 21 大,往后面找
(2) 比較 37, 比 37大,比鏈表最大值小,從 37 的下面一層開始找
(3) 比較 71, 比 71 大,比鏈表最大值小,從 71 的下面一層開始找
(4) 比較 85, 比 85 大,從后面找
(5) 比較 117, 等於 117, 找到了節點。
具體的搜索算法如下:
- /* 如果存在 x, 返回 x 所在的節點,
- * 否則返回 x 的后繼節點 */
- find(x)
- {
- p = top;
- while (1) {
- while (p->next->key < x)
- p = p->next;
- if (p->down == NULL)
- return p->next;
- p = p->down;
- }
- }
跳表的插入
先確定該元素要占據的層數 K(采用丟硬幣的方式,這完全是隨機的)
然后在 Level 1 ... Level K 各個層的鏈表都插入元素。
例子:插入 119, K = 2
如果 K 大於鏈表的層數,則要添加新的層。
例子:插入 119, K = 4
丟硬幣決定 K
插入元素的時候,元素所占有的層數完全是隨機的,通過一下隨機算法產生:
- int random_level()
- {
- K = 1;
- while (random(0,1))
- K++;
- return K;
- }
相當與做一次丟硬幣的實驗,如果遇到正面,繼續丟,遇到反面,則停止,
用實驗中丟硬幣的次數 K 作為元素占有的層數。顯然隨機變量 K 滿足參數為 p = 1/2 的幾何分布,
K 的期望值 E[K] = 1/p = 2. 就是說,各個元素的層數,期望值是 2 層。
跳表的高度。
n 個元素的跳表,每個元素插入的時候都要做一次實驗,用來決定元素占據的層數 K,
跳表的高度等於這 n 次實驗中產生的最大 K,待續。。。
跳表的空間復雜度分析
根據上面的分析,每個元素的期望高度為 2, 一個大小為 n 的跳表,其節點數目的
期望值是 2n。
跳表的刪除
在各個層中找到包含 x 的節點,使用標准的 delete from list 方法刪除該節點。
例子:刪除 71
————————————————————轉載結束—————————————————————
隨機的層高level-相關資料
http://blog.csdn.net/kisimple/article/details/38706729
http://blog.csdn.net/unix21/article/details/10197115