資料結構與演算法 跳表

2021-09-20 04:03:44 字數 1821 閱讀 8571

二分查詢利用靜態陣列隨機訪問的特性,可以實現在有序的陣列中快速找到某個值,但是因為靜態陣列需要申請連續的記憶體空間,所以當資料規模比較大時,在記憶體中可能無法申請到所需的連續空間。因此,基於這一特性,我們考慮能否將二分查詢應用於鍊錶結構,這樣就避免連續空間的限制,但是對於鍊錶結構,怎樣提高它的查詢效率是解決該問題的關鍵。通過給鍊錶建立多級索引的方式來對其進行改造,再與二分查詢相結合就可以大大的提公升查詢效率這種改造的鍊錶我們稱之為跳表

如下圖所示,我們每隔兩個結點,選取乙個結點作為索引層的元素,這樣第一級索引層的結點個數,為n/2。假如,我們要找16,我們首先可以遍歷索引層,當遍歷到13時,檢測它的下乙個結點為17,所以確定16在它們之間,13結點通過down指標找到原始鍊錶中13的位置,再繼續向後查詢找到16。可以對比一下如果沒有建立索引,需要遍歷10個結點,有了索引層之後只需遍歷7個就可以找到。因此得出的結論是查詢的效率變高了

上面的例子我們感覺查詢提公升的速度並不是很多,因此我們可以按照之前的思路,再建立一層索引,結果如下圖所示。

同樣查詢16,上圖的方式僅需遍歷6個結點。如果資料規模更大,該方法提公升查詢速度的效果會更加明顯。

我們都知道單鏈表中查詢某個資料的時間複雜度是o(n)。現在我們來分析一下多級索引的跳表查詢某個資料的時間複雜度。假設鍊錶中有n個結點,如果按照每兩個結點抽出乙個結點作為上一級索引的結點,那麼第一級索引的結點個數大約是n/2,第二級索引的結點個數大約就是n/4,第**索引的結點個數大約就是n/8,依次類推,也就是說,第k級索引的結點個數是第k-1級索引的結點個數的1/2,那第k級索引結點的個數就是n/(2^k)。

假設索引有h級,最高端的索引有2個結點。通過上面的通項公式可有n/(2^h) = 2,從而求得h = logn - 1。如果包括原始的鏈表層,整個鍊錶的高度就是logn。如果每層我們需要遍歷m個結點,則在跳表中查詢乙個資料的時間複雜度就是o(m*logn)。分析可知,每層最多隻需遍歷3個結點,因此最終的時間複雜度為o(3logn)即o(logn)。

因為跳表的查詢時間複雜度是o(logn),而對於鍊錶的插入而言,單純的插入某乙個資料的時間複雜對為o(1),因此我們可以知道在跳表中插入某一資料的時間複雜度也為o(logn)。具體插入某一資料的過程如下圖所示

刪除跳表中某一結點的某一資料時,與刪除鍊錶中的某一資料類似,只不過查詢該資料的速度更快。還需要注意的如果原始鍊錶為單鏈表,在刪除某一結點時,需要找到其前驅結點,時間複雜度同樣為o(logn)。

當我們不停的往跳表中插入資料時,如果我們不更新索引,就有可能某2個索引結點之間的資料非常多的情況。極端情況下,跳表會退化為單鏈表。作為動態資料結構,我們需要某種手段來維護索引與原始鍊錶大小之間的平衡,也就是說,如果鍊錶中結點個數增加,索引結點就應該相應地增加一些,避免時間複雜度退化,以及查詢、刪除、插入操作效能下降。跳表是通過隨機函式來維護前面提到的「平衡性」。通過隨機函式,來決定將插入結點,可以插入到哪幾級索引中,比如隨機生成了值k,那我們就將這個結點新增到第一級到第k級這k級索引中。舉個栗子,如下圖所示

關於隨機函式的選擇,以及實現我還沒有深入研究,後面研究明白了會繼續和大家分享。未完待續……

資料結構與演算法 跳表

回顧上節 上節課中我們學習了二分法查詢,最基本的二分法查詢需要隨機的訪問資料,底層都是基於陣列的儲存結構 1 思考問題,如果底層是基於鍊錶的方式儲存資料.是否能用二分法查詢呢?我們只要對陣列進行稍微改造,基於鍊錶實現,並在鍊錶的基礎上分別建立對於的索引,就可以快速的基於鍊錶的方式進行查詢,而且該種方...

資料結構與演算法 跳表

解決鍊錶查詢時耗時過長的問題。英文全稱 skip list 鍊錶 多級索引 鍊錶 跳表 顧名思義,跳表的查詢是在多個鍊錶之間跳躍查詢的,其路線類似於走台階,如下圖所示 舉個栗子 某一時刻,想查詢代號為 8 的節點的資料,按照常規鍊錶查詢,需要從最左側挨個查詢至最右側,遍歷次數為 8 時間複雜度為 o...

演算法 跳表資料結構

關於跳表 跳表作為一種特殊的有序單鏈表結構,由於鍊錶本身並不支援二分查詢,而在跳表結構中其通過維護多級索引的方式來實現快速查詢 類似於二分查詢 對於其索引的結構為存在兩個指標,分別是next指標指向同一層級的下乙個節點 同時存在乙個down指標指向下一層相同位置 以及相同資料 的節點 通過增加多級索...