紅黑樹
紅黑樹本質上是二叉平衡查找樹,二叉平衡查找樹有很多種,包括AVL樹,Splay Tree(伸展樹)、Treap(樹堆)等
紅黑樹是平衡二叉查找樹中的一種,也是最常用的一種。
二叉查找樹在頻繁的動態更新過程中,可能會出現樹的高度遠大於 log2n 的情況,從而導致各個操作的效率下降。
極端情況下,二叉樹會退化爲鏈表,時間複雜度會退化到 O(n)。
要解決頻繁動態更新後複雜度退化的問題,我們就需要平衡二叉查找樹。
平衡二叉查找樹
平衡二叉樹的嚴格定義是這樣的:二叉樹中任意一個節點的左右子樹的高度相差不能大於 1
首先,滿二叉樹和完全二叉樹肯定都是平衡二叉樹,但是非完全二叉樹也可能是平衡二叉樹。
最先被髮明的平衡二叉查找樹是AVL樹。
它嚴格符合我剛講到的平衡二叉查找樹的定義,即任何節點的左右子樹高度相差不超過 1,是一種高度平衡的二叉查找樹。
但實際上很多平衡二叉查找樹其實並沒有嚴格符合上面嚴格的定義。
就比如說紅黑樹,它從根節點到各個葉子節點的最長路徑,有可能會比最短路徑大一倍。
“平衡”
平衡二叉查找樹中“平衡”的意思,其實就是讓整棵樹左右看起來比較“對稱”、比較“平衡”。
不要出現左子樹很高、右子樹很矮的情況。
這樣就能讓整棵樹的高度相對來說低一些,相應的插入、刪除、查找等操作的效率高一些。
所以說一個平衡二叉樹,只要樹的高度不比 log2n 大很多(比如樹的高度仍然是對數量級的)。
儘管它不符合嚴格的平衡二叉查找樹的定義,但仍然可以說,這是一個合格的平衡二叉查找樹。
紅黑樹的要求
紅黑樹的英文是“Red-Black Tree”,簡稱 R-B Tree。它是一種不嚴格的平衡二叉查找樹。
之所以叫紅黑樹,是因爲紅黑樹中的節點,一類被標記爲黑色,一類被標記爲紅色。
除此之外,還有很多的要求:
一、 根節點是黑色的;
二、 每個葉子節點都是黑色的空節點(NIL),也就是說,葉子節點不存儲數據;
三、 任何相鄰的節點都不能同時爲紅色,也就是說,紅色節點是被黑色節點隔開的;
四、 每個節點,從該節點到達其可達葉子節點的所有路徑,都包含相同數目的黑色節點;
“近似平衡”
紅黑樹是一種近似平衡的二叉查找樹。
“平衡”的意思可以等價爲性能不退化。“近似平衡”就等價爲性能不會退化的太嚴重。
二叉查找樹很多操作的性能都跟樹的高度成正比。衡量平衡與否可以概念轉換一下。
一棵極其平衡的二叉樹(滿二叉樹或完全二叉樹)的高度大約是 log2n。
所以如果要證明紅黑樹是近似平衡的,我們只需要分析,紅黑樹的高度是否比較穩定地趨近 log2n 就好了。
實現紅黑樹的基本思想
魔方的復原解法是有固定算法的:遇到哪幾面是什麼樣子,對應就怎麼轉幾下。
你只要跟着這個復原步驟,就肯定能將魔方復原。
紅黑樹的平衡過程跟魔方復原非常神似,大致過程就是:遇到什麼樣的節點排布,我們就對應怎麼去調整。
只要按照固定的操作步驟,保持插入、刪除的過程,不破壞平衡樹的定義就行了。
左旋(rotate left)、右旋(rotate right)
左旋全稱其實是叫圍繞某個節點的左旋,右旋就叫圍繞某個節點的右旋。
左旋和右旋是紅黑樹進行平衡調整的基本操作。
左旋和右旋其實可以想象成一個定滑輪。
圍繞節點 X 的左旋:
拉住節點 X 左邊的 a 子樹,向下拉,將節點 Y 拉到節點 X 的位置,a 子樹仍是節點 X 的左子樹,而節點 X 成爲了節點 Y 的左子節點,那麼就將原先節點 Y 的左子樹 b 摘下來,變成節點 X 的右子樹;
同理,圍繞節點 X 的右旋:
拉住節點 X 右邊的 r 子樹,向下拉,將節點 Y 拉到節點 X 的位置,a 子樹仍是節點 Y 的左子樹,而節點 X 成爲了節點 Y 的右子節點,那麼就將原先節點 Y 的右子樹 b 摘下來,變成節點 X 的左子樹;
此時關注節點是X,在紅黑樹平衡調整的過程中,一定要找準關注節點,不要搞丟、搞錯關注節點。