紅黑樹、散列表、跳表理解入門


跳表

跳表是什么?

就是把鏈表的結構稍加改造,這種數據結構叫 \color{red}{跳表}

為什么要改造鏈表呢?

為了提升鏈表的查詢效率,怎么讓鏈表支持類似‘數組’那樣的‘二分’算法呢

簡單理解跳表

跳表是一個各方面性能都比較優秀的 動態數據結構,可以支持快速地插入、刪除、查找操作,寫起來也不復雜,甚至可以替代紅黑樹。

Redis 中的有序集合(Sorted Set)就是用跳表來實現的。
那 Redis 為什么會選擇用跳表(和散列表)來實現有序集合呢? 為什么不用紅黑樹呢?這個問題一會在回答,先看看跳表的數據結構

跳表數據結構

其實概念很簡單,就是在鏈表上加上了 \color{red}{索引層}

 
跳表數據結構

是不是很像二分法呢,如果每兩個結點會抽出一個結點作為上一級索引的結點,最后留下2個結點,那時間復雜度就是 O(logn)
這個查找的時間復雜度跟二分查找是一樣的,不過對於單鏈表是用空間換來的。也可能通過每3個結點抽出一個做為索引,也可以極大的節省內存。

 

為什么叫動態數據結構

當我們在不停插入數據,如果我們不更新索引,可能出現某 2 個索引結點之間數據非常多的情況。極端情況下,跳表還會退化成單鏈表。
紅黑樹、AVL 樹這樣平衡二叉樹,是通過左右旋的方式保持左右子樹的大小平衡,而跳表是通過隨機函數來維護平衡性。

插入、刪除、查找以及迭代輸出有序序列這幾個操作,紅黑樹也可以完成,時間復雜度跟跳表是一樣的。但是,按照區間來查找數據這個操作,紅黑樹的效率沒有跳表高。

對於按照區間查找數據這個操作,跳表可以做到 O(logn) 的時間復雜度定位區間的起點,然后在原始鏈表中順序往后遍歷就可以了。

PS: B+樹就把葉子節點連起來

Redis 鍵值構建一個散列表,這樣按照 key 來刪除、查找一個成員對象的時間復雜度就變成了 O(1)。同時,借助跳表結構,其他操作也非常高效。


散列表

散列表的英文叫“Hash Table”,我們平時也叫它“哈希表”或者“Hash 表”

\color{red}{散列表用的是數組支持按照下標隨機訪問數據的特性,}
\color{red}{所以散列表其實就是數組的一種擴展,由數組演化而來。}
\color{red}{可以說,如果沒有數組,就沒有散列表}

散列技術是在記錄的存儲位置和它的關鍵字之間建立一個確定的對應關系 f,使得每個關鍵字 key 對應一個存儲位置 f(key)。查找時根據這個對應關系匠互給定的 key 的映射 f(key)

存儲位置 = f(關鍵字)

這種關系 f 稱為散列函數(又稱哈希函數)。散列技術將記錄存儲在一塊連續的存儲空間中,這塊連續存儲空間稱為散列表或哈希表。那么關鍵字對應的記錄存儲位置稱為散列地址。

散列函數的構造方法

散列函數的構造方法特點就是:計算簡單、散列地址分布均勻

  • 直接定址法
  • 數學分析法
  • 平方取中法
  • 折疊法
  • 除留余數法
  • 隨機數法

大家一定聽說過 hash 碰撞。就是2個不同的 key 對應着不同的 f 關系。但這是幾乎不可能的,即便像業界著名的MD5、SHA、CRC等哈希算法,也無法完全避免這種散列沖突。而且,因為數組的存儲空間有限,也會加大散列沖突的概率。

散列沖突

我們只能通過其它途徑來尋找方法。我們常用的散列沖突解決方法有兩類,開放尋址法(open addressing)和鏈表法(chaining)。

開放尋址法(open addressing)

所謂的開放尋址法就是一但發生了沖突,就去尋找下一個空的散地址,只要散列表足夠大,空的散列表地址總能找到,並將記錄存入。

 
image.png
 
公式

鏈表法(chaining)

鏈地址法又稱鏈表法,其實當發生沖突時存入鏈表,如下圖很容易就可以看明白。此時,已經不存在什么沖突地址的問題,無論有多少沖突,都只是在當前位置給單鏈表增加結點的問題。

 
image.png

公共益出區法

這種不常見,就是把沖突的單獨找個地方。


紅黑樹

顧名思義,紅黑樹中的節點,一類被標記為黑色,一類被標記為紅色。除此之外,一棵紅黑

平衡二叉樹 是一種二叉排序樹,其中每一個節點的左子樹和右子樹的高度不能大於 1

紅黑樹是一種平衡二叉查找樹。它是為了解決普通二叉查找樹在數據更新的過程中,復雜度退化的問題而產生的。紅黑樹的高度近似 log2n,所以它是近似平衡,插入、刪除、查找操作的時間復雜度都是 O(logn)。

平衡二叉查找樹其實有很多,比如,Splay Tree(伸展樹)、Treap(樹堆)等,但是我們提到平衡二叉查找樹,聽到的基本都是紅黑樹。
紅黑樹在眾多里面,表現的最為平衡。
“近似平衡”就等價為性能不會退化得太嚴重。

一棵紅黑樹還需要滿足這樣幾個要求:

  • 根節點是黑色的;
  • 每個葉子節點都是黑色的空節點(NIL),也就是說,葉子節點不存儲數據;
  • 任何相鄰的節點都不能同時為紅色,也就是說,紅色節點是被黑色節點隔開的;
  • 每個節點,從該節點到達其可達葉子節點的所有路徑,都包含相同數目的黑色節點;

看到這里你會很頭大,什么黑的紅的,完全不懂。賦上連接,有時間在看

總結

散列表:插入刪除查找都是O(1), 是最常用的,但其缺點是不能順序遍歷(存入的數據是無順序的)以及擴容縮容的性能損耗。適用於那些不需要順序遍歷,數據更新不那么頻繁的。
散列表總和鏈表、跳表一起出現組合使用。

跳表:插入刪除查找都是O(logn), 並且能順序遍歷。缺點是空間復雜度O(n)。適用於不那么在意內存空間的,其順序遍歷和區間查找非常方便。
跳表還可以和散列表組合讓刪除、查找一個成員對象操作變為O(1),也就是說利用了散列表查找速度,跳表的順序結構

紅黑樹:插入刪除查找都是O(logn), 中序遍歷即是順序遍歷,穩定。缺點是難以實現,去查找不方便。其實跳表更佳,但紅黑樹已經用於很多地方了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM