紅黑樹的條件是怎麼來的——從2-3樹到紅黑樹
前幾天學習epoll函式,發現epoll函式使用紅黑樹結構儲存epfd來提高fd檢索速度,又想起HashMap中引入了紅黑樹在達到一定連結串列長度後轉為紅黑樹來避免連結串列遍歷。
紅黑樹的應用這麼廣泛,我們應該對其有所瞭解。
首先我們應該知道,在無序線性表中查詢是否存在某個值需要對線性表進行遍歷,其查詢時間複雜度為On,但如果線性表是有序的,使用二分法查詢,其時間複雜度就會降低為Ologn,讓待查詢的線性表保持有序顯然是有利於查詢操作的,但這種便利並不是沒有代價的,不論是各種排序演算法,還是在構建線性表時就保持有序,都會帶來額外的操作,例如MysqlnnoDB使用的多路查詢平衡B+樹,又或者是我們最熟悉的平衡二叉樹,其維護有序的成本都很高,而紅黑樹,想必大家早就有所耳聞或有所瞭解,接下來我們詳細介紹這種排序樹好在哪裡,本篇純屬個人理解原創,做交流記錄用,僅供參考。
什麼是紅黑樹,紅黑樹的要求有哪些,本文不再贅述,網上找到的文章都有答案,瞭解到紅黑樹的結構和條件後,會發現,與二叉平衡樹非常相似,只是平衡的條件和要求不同,這也是我一直以來的疑惑,二叉平衡樹的邏輯很好理解,通過旋轉來達到平衡條件,使得最大查詢長度為logn,那為什麼epoll和hashMap不直接使用AVL樹呢?我認為最終要的原因就是AVL的平衡條件太苛刻,左右子樹的高度差不能超過1,否則就要出發複雜的rebalance操作,從實際使用的角度來看,在n較大的情況下logn和logn + 1的查詢效率差別不大,反而因為苛刻條件而導致的開銷不能功過相抵,在寫操作頻繁的操作中,AVL似乎並不能很好的適應,換句話說,AVL樹太完美了,也太敏感了。
相信大家和我有一樣的疑惑,紅黑樹的那些平衡條件是怎麼總結出來的,似乎並不是AVL這種順應邏輯的條件,更像是推匯出來的條件,因此,我們從rbt的近親--2-3樹說起。
2-3樹,大家也有所耳聞,2-3樹存在三種節點,第一種是2-節點,資料域有一個值,指標域有兩個值;第二種是3-節點,資料域有2個值,指標域有3個值;還有一種是空節點,如下圖所示:
2-3樹的插入和構建過程如下圖所示,可參考:https://www.cnblogs.com/eniac12/p/5558848.html,介紹的很詳細
可以知道的是,2-3樹在插入過程中不斷增長出新的根,使得樹的高度不斷增高,底層節點高度永遠相等,忽略2節點和3節點的差異後會發現這是一顆完全二叉樹,
所有節點的高度都相等,是一顆完美的AVL樹
如果我們將其全部的3節點展開,展開時通過紅色邊連結展開後的子節點構造成一顆二叉樹,將上圖最終構建的2-3樹按照剛才的思路展開如下圖所示:
如果我們把當前節點同父節點之間的邊的顏色計作節點的顏色,則會變成下圖所示:
這就是這顆2-3樹唯一對應的rbt,由上圖展開前的情況可知,未展開紅色節點前,所有節點的高度完全一致,展開紅色節點後,忽略掉紅色節點,所有節點的黑高就是完全一致的,
這就是紅黑二叉樹的性質之一:所有節點的黑高一致。
由展開過程和分配節點顏色可知只有父節點原來是3節點時,才會將一個父節點中的一個染色為紅色,但是展開後,該紅色父節點變成了2節點,不會對該節點進行展開操作,所以該節點的子節點一定是黑色,
這就是紅黑樹的第二條性質:不會存在連續的兩個紅色節點,或者說,紅色節點的子節點必為黑色節點。
根據這些性質,保證了展開後的紅黑樹的最長查詢路徑不會超過最短路徑的2倍,同時沒有AVL樹那麼敏感,像上圖插入10號節點後已經不滿足AVL的定義需要調整了,在操作節點頻繁的情況下,rbt帶來的開銷要比AVL小。
如果是查多操作少的情況,則AVL樹的穩定性更好。
Yesterday You Said Tomorrow