1. 程式人生 > >演算法-跳錶

演算法-跳錶

為什麼 Redis 要用跳錶來實現

什麼是跳錶

二分查詢中,底層資料結構是陣列。如果底層資料結構換成連結串列,使其支援二分查詢,那麼就城這種資料結構為跳錶。

跳錶是一種各方面效能都比較優秀的動態資料結構。它支援快速的查詢,插入以及刪除操作,實現起來也不復雜,甚至可以替代紅黑樹。

理解跳錶

原始連結串列{1,3,4,5,7,8,9,10,13,16,17,18}
一級索引{1,4,7,9,13,17}
二級索引{1,7,13}

為了快速查詢,對連結串列建立索引。首先對當前層級,每兩個元素抽出一個,作為上級資料(索引層)。例如,對原始資料集合建立索引的索引就是一級索引。

在一級索引上查詢元素 16,遍歷一級索引到元素 13 的位置,發現元素 16 在區間 13 到 17 之間。然後跳轉到原始連結串列,從元素 13 這個節點開始往後找,最終找到元素 16 的節點。在這個過程中,一共遍歷節點 6 個。如果沒有索引,那麼在原始連結串列中找到元素 16 需要遍歷10 個節點。

如果在一級索引上再次建立索引,每兩個元素提取一個,組成下一級索引層就是二級索引。在二級索引上查詢元素 16,遍歷一級索引到元素 13 的位置,然後跳轉到一級索引,發現元素 16 在區間 13 到 17 之間。然後跳轉到原始連結串列,從元素 13 這個節點開始往後找,最終找到元素 16 的節點。在這個過程中,一共遍歷節點 4 個。如果沒有索引,那麼在原始連結串列中找到元素 16 需要遍歷10 個節點。

上述例子中,資料量比較小,但是也足以讓我們看出來構建過索引之後,再查詢某個元素,效率已經提升了。這種連結串列加多級索引的結構,就是跳錶。

跳錶查詢時間複雜度分析

根據剛才的距離,可以發現沒增加一級索引,該索引層的節點個數就是上一層及的二分之一。所以可以得出每層索引元素的個數:n/2,n/4,n/8…2/(n^k),其中 n 為初始資料元素個數,k 為索引層級。

假設索引層級為 h ,最高索引有兩個節點,那麼 n/(2^h) = 2,h = log2|n - 1。在跳錶查詢某個資料時,每一層要遍歷 m 個節點,那麼查詢到某個資料,時間複雜度就是 O(m*logn)。

那麼 m 值等於多少呢?根據剛才構建索引的資料結構來看,當指標節點從上一層索引轉移到下一層索引的時候,上層索引區間對應的下層索引資料,只有三個元素,所以 m = 3。例如上面跳錶,查詢值為 8 的元素。在二級索引中,指標在 7 的位置需要跳轉到一級索引查詢,但是我們已經明確要查詢的元素就在 7~13 的區間,指標跳轉到一級索引的時候,7~13 這個區間只有三個元素,當指標跳轉到初始資料的時候,7~9 這個區間也只有三個元素。所以,類推出結論:按照每兩個元素提取一個構建的索引,每一次最多遍歷三個元素,所以 m = 3,最終的時間複雜度就是 O(*logn)。

跳錶的記憶體佔用

上述描述中,我們已經推匯出每一層索引節點的個數,那麼索引層總共建立的節點個數是:2/n + 4/n + … + 4 + 2 = n - 2。所以,跳錶的空間複雜度為 O(n)。雖然時間複雜度為 O(n),但是實際開發工程中,我們構建索引的時候,並不會把整個物件拿出來構建索引,而是利用物件的個別屬性的值建立索引。所以,跟整個物件佔用的記憶體來比,依據屬性值建立的索引,佔據的空間就很小了。

跳錶的查詢刪除操作時間複雜度

首先,我們要找到要插入或者要刪除的節點的位置,時間複雜度已經是 O(logn)。對於雙向連結串列來說,插入刪除操作時間複雜度是 O(1),所以雙向連結串列實現的跳錶資料結構,插入刪除操作的時間複雜度也是O(logn)。

跳錶索引的動態更新

關於這一點,我理解的還是不太透徹。王爭老師闡述的是利用一個隨機函式,要插入一個節點 6,如果隨機函式生成值 k ,那麼就在一級到 k 級索引中全部插入節點 6 的索引。從概率上講,最終生成的索引也將是均勻分佈的。

為什麼 Redis 要用跳錶來實現

Redis 的有序集合是通過跳錶來實現的,嚴格的講,還用到了散列表。這就如同開發語言中的排序方法,針對不同的資料規模,會有不同的排序實現。

Redis 有序集合的核心操作:插入,刪除,查詢,按區間查詢,迭代輸出有序序列。按照區間查詢,紅黑樹實現的效率要低於跳錶。

跳錶相對於紅黑樹程式碼更容易實現,容易讓人理解。跳錶更加靈活,可以通過改變索引構建策略,有效平衡執行效率和記憶體消耗。

多數程式語言的 Map 就是基於紅黑樹實現的,使用起來更加方便。而跳錶沒有現成的實現,需要自己開發。

總結

本文創作靈感來源於 極客時間 王爭老師的《資料結構與演算法之美》課程,通過課後反思以及借鑑各位學友的發言總結,現整理出自己的知識架構,以便日後溫故知新,查漏補缺。

初入演算法學習,必是步履蹣跚,一路磕磕絆絆跌跌撞撞。看不懂別慌,也別忙著總結,先讀五遍文章先,無他,唯手熟爾~
與諸君共勉

關注本人公眾號,第一時間獲取最新文章釋出,每日更新一篇技術文章。

在這裡插入圖片描述