数据结构三——跳表
文章出處:極客時間《數據結構和算法之美》-作者:王爭。該系列文章是本人的學習筆記。
跳表的由來
說明:圖片來自極客時間
由來
二分查找的數據結構是數組,利用數組隨機訪問的特定查找的時間復雜度是O(logn)。如果數據結構是鏈表,可以達到這樣的速度嗎?答案是可以的。只是要改造。改造之后的結構就是跳表,是一種動態數據結構,可以支持快速的插入、刪除、查找、按范圍查找。功能類似于紅黑樹。Redis中的有序集合使用的就是跳表。
跳表的結構
對于單鏈表來說,存儲的數據是有序的,想要查找某個數,時間復雜度是O(n)。如果對單鏈表建一個一級“索引”,就是說每兩個節點提取一個節點。提取出的節點有一個down指針指向原始鏈表中的同一個節點。
現在要查找數據16,那么查找路徑是:1,4,7,9,13,13(原始鏈表),16。查找7個節點。單鏈表查找16需要查找10個節點。
如果對一級索引再建“索引”,形成二級索引。
現在要查找數據16,那么查找路徑是:1,7,13,13,13,16。查找6個節點。
當n小的時候,減少的節點數量不明顯。如果是n=64。建5級索引。
現在查找62需要11個節點,路徑是1,33,33,49,49,57,57,61,61,61,62。原來需要62個節點。提升效果很明顯。當n越大,提升效果越明顯。
跳表=鏈表+多級索引
跳表的時空復雜度
時間復雜度
當節點個數為n的時候,跳表會建幾層索引呢?第1級索引節點個數n2\dfrac{n}{2}2n?,第2級索引節點個數n4\dfrac{n}{4}4n?,第k級索引節點個數n2k\dfrac{n}{2^k}2kn?。最上面一層索引節點個數是2。也就是說2=n2l2=\dfrac{n}{2^l}2=2ln?,l+1=log2nl+1=log_2nl+1=log2?n,l=log2n?1l=log_2n-1l=log2?n?1。再加上原始鏈表層,跳表有log2nlog_2nlog2?n層,記為logn。
每一層最多查詢的節點個數是3。因為在建每一層索引的時候,是每2個數據建一個節點。例如查找數據x,在第k層發現y<x,x>zy<x,x>zy<x,x>z,所以通過y的down指針向,從第k層走到第k-1層。而y和z節點之間最多有3個節點(包含y和z)。
那么跳表的時間復雜度就是O(logn)。和二分查找是同樣的查找效率。
空間復雜度
鏈表的查找速度和二分一樣,這是需要付出空間代價的。也就是以空間換時間。那么額外需要多少空間呢?n2+n4+n8+...+2=n\dfrac{n}{2}+\dfrac{n}{4}+\dfrac{n}{8}+...+2=n2n?+4n?+8n?+...+2=n,等比數列求和。所以空間復雜度是O(n)。
跳表插入和刪除
插入
對于插入來講,為了維持鏈表的有序性,在插入一個數據的時候需要先查找到插入的位置。
例如在鏈表中插入6,需要查找插入位置,查找節點1,1,4,4,5,時間復雜度和查找一個數字類似,O(logn)。鏈表的插入操作是O(1),所以整體插入操作時間復雜度O(logn)。
刪除
刪除操作不僅要刪除鏈表層,同時需要刪除索引層的節點。時間復雜度O(logn)。
動態索引表更新
索引更新
當不斷插入數據的時候,如果不更新索引層,極端情況下跳表退化為單鏈表。當插入數據的時候,同時更新某些索引層。至于在哪些層建索引,可以通過隨機函數來選擇。
思考題
1 redist為什么使用跳表而不是紅黑樹?
redist的核心操作是:
插入一個數據;
刪除一個數據;
查找一個數據;
按范圍查找一個區間內的數據;
迭代輸出有序序列
紅黑樹效率不高的操作是:按范圍查找一個區間內的數據。
其他原因:跳表更容易實現,代碼比較簡單。
2 如果每3個或者5個節點抽取一個做索引,那么跳表的時間復雜度和空間復雜度是多少呢?
如果每三個或者五個節點提取一個節點作為上級索引,那么對應的查詢數據時間復雜度,也還是 O(logn)。其實這里的底數已經不是2,而是3或者5。
空間復雜度,也依然是一個等比數列的和:n3+n9+n8+...+3=32(n?1)\dfrac{n}{3}+\dfrac{n}{9}+\dfrac{n}{8}+...+3=\dfrac{3}{2}(n-1)3n?+9n?+8n?+...+3=23?(n?1),記為O(n)。
總結
- 上一篇: 修改锁的公平性
- 下一篇: Eclipse更改字体大小