一.爲何要有紅黑樹這種數據結構?html
學過二叉查找樹的同窗都知道,普通的二叉查找樹在極端狀況下可退化成鏈表,此時的增刪查O(n)效率都會比較低下。爲了不這種狀況,就出現了一些自平衡的查找樹,好比 AVL。segmentfault
ALV樹是一種嚴格按照定義來實現的平衡二叉查找樹,因此它查找的效率很是穩定,爲O(log n),因爲其嚴格按照左右子樹高度差不大於1的規則,插入和刪除操做中須要大量且複雜的操做來保持ALV樹的平衡(左旋和右旋),所以ALV樹適用於大量查詢,少許插入和刪除的場景中。數據結構
如今假設有這樣一種場景:大量查詢,插入和刪除,使用ALV樹就不太合適了,由於ALV樹大量的插入和刪除會很是耗時間,那麼咱們是否能夠下降ALV樹對平衡性的要求從而達到快速的插入和刪除呢?性能
答案確定是有的,紅黑樹這種數據結構就應運而生了(由於ALV樹是高度平衡的,因此查找起來確定比紅黑樹快,可是紅黑樹在插入和刪除方面的性能就遠遠不是ALV樹所能比的了)。ui
二.紅黑樹的性質spa
紅黑樹經過以下的性質定義實現自平衡:.net
1.節點是紅色或黑色。
2.根是黑色。
3.全部葉子都是黑色(葉子是NIL節點)。
4.每一個紅色節點必須有兩個黑色的子節點。(從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點。)
5.從任一節點到其每一個葉子的全部簡單路徑都包含相同數目的黑色節點(簡稱黑高)。
有了上面的幾個性質做爲限制,便可避免二叉查找樹退化成單鏈表的狀況。可是,僅僅避免這種狀況還不夠,這裏還要考慮某個節點到其每一個葉子節點路徑長度的問題。若是某些路徑長度過長,那麼,在對這些路徑上的及誒單進行增刪查操做時,效率也會大大下降。這個時候性質4和性質5用途就凸顯了,有了這兩個性質做爲約束,便可保證任意節點到其每一個葉子節點路徑最長不會超過最短路徑的2倍。3d
當某條路徑最短時,這條路徑必然都是由黑色節點構成。當某條路徑長度最長時,這條路徑必然是由紅色和黑色節點相間構成(性質4限定了不能出現兩個連續的紅色節點)。而性質5又限定了從任一節點到其每一個葉子節點的全部路徑必須包含相同數量的黑色節點。此時,在路徑最長的狀況下,路徑上紅色節點數量 = 黑色節點數量。該路徑長度爲兩倍黑色節點數量,也就是最短路徑長度的2倍。舉例說明一下,請看下圖:code
三.紅黑樹的基本操做之旋轉htm
旋轉操做分爲左旋和右旋,左旋是將某個節點旋轉爲其右孩子的左孩子,而右旋是節點旋轉爲其左孩子的右孩子。這話聽起來有點繞,因此仍是請看下圖:
上圖包含了左旋和右旋的示意圖,這裏以右旋爲例進行說明,右旋節點 M 的步驟以下:
四.紅黑樹的基本操做之添加元素
紅黑樹的插入過程和二叉查找樹插入過程基本相似,不一樣的地方在於,紅黑樹插入新節點後,須要進行調整,以知足紅黑樹的性質。性質1規定紅黑樹節點的顏色要麼是紅色要麼是黑色,那麼在插入新節點時,這個節點應該是紅色仍是黑色呢?答案是紅色,緣由也不難理解。若是插入的節點是黑色,那麼這個節點所在路徑比其餘路徑多出一個黑色節點,這個調整起來會比較麻煩(參考紅黑樹的刪除操做,就知道爲啥多一個或少一個黑色節點時,調整起來這麼麻煩了)。若是插入的節點是紅色,此時全部路徑上的黑色節點數量不變,僅可能會出現兩個連續的紅色節點的狀況。這種狀況下,經過變色和旋轉進行調整便可,比以前的簡單多了。
如今咱們來分析一下新增的節點(紅色)插入以後可能面臨的幾種狀況,以及他們的處理措施:
1.插入的節點爲根節點
新插入的紅色節點變成黑色節點,知足根節點爲黑色節點的要求
2.父親節點爲黑色節點
這個時候不須要進行任何調整操做,此時的樹仍然是一顆標準的紅黑樹
3.父親節點爲紅色節點的狀況下,叔叔節點爲紅色節點(不用考慮左右)
解決方案:將叔叔和父親節點改成黑色,爺爺節點改成紅色,而後又將爺爺節點看成插入節點看待,一直進行上面的操做,直到當前節點爲根節點,而後將根節點變成黑色
4.父親節點爲紅色,叔叔節點爲黑色
1)父親節點爲爺爺節點的左孩子,新插入節點爲父節點的左孩子(左左)
解決方案:將父親節點和爺爺節點顏色互換(父節點變爲黑色,爺爺節點變爲紅色),而後對爺爺節點進行一次右旋
注:上圖叔叔是空葉子節點,因此也是黑色
2)父親節點爲爺爺節點的右孩子,新插入節點爲父節點的右孩子(右右)
解決方案:將父親節點和爺爺節點顏色互換(父節點變爲黑色,爺爺節點變爲紅色),而後對爺爺節點進行一次左旋
3)父親節點爲爺爺節點的左孩子,新插入節點爲父節點的右孩子(左右)
解決方案:對父親節點進行一次左旋,而後就變成了狀況1,按照狀況1再進行處理
4)父親節點爲爺爺節點的右孩子,新插入節點爲父節點的左孩子(右左)
解決方案:對父親節點進行一次右旋,而後就變成了狀況2,按照狀況2再進行處理
五.紅黑樹的基本操做之刪除元素
相較於插入操做,紅黑樹的刪除操做則要更爲複雜一些。刪除操做首先要肯定待刪除節點有幾個孩子,若是有兩個孩子,不能直接刪除該節點。而是要先找到該節點的前驅(該節點左子樹中最大的節點)或者後繼(該節點右子樹中最小的節點),而後將前驅或者後繼的值複製到要刪除的節點中,最後再將前驅或後繼刪除。因爲前驅和後繼至多隻有一個孩子節點,這樣咱們就把原來要刪除的節點有兩個孩子的問題轉化爲只有一個孩子節點的問題,問題被簡化了一些。
刪除一個節點有如下四種狀況:
1.刪除的節點沒有孩子
2.刪除的節點只有左子樹
3.刪除的節點只有右子樹
*4.刪除的節點擁有左子樹和右子樹
其實只有上面前三種狀況,對於第四種狀況,能夠找到待刪除節點的直接後繼節點,用這個節點的值替代待刪除節點,接着狀況轉變爲刪除這個直接後繼節點,狀況也變爲前三種之一。
1.刪除的節點只有左子樹或只有右子樹
或者
只有上面兩種狀況會存在於紅黑樹中,直接用DL/DR的元素值代替D的元素,再把DL/DR直接刪去就好。
2.刪除的節點沒有孩子
1)待刪除節點是紅色的,直接刪去這個節點。
2)父節點P是紅色節點
解決方案:把P節點染成黑色,兄弟節點染成紅色,刪除節點D。
3)兄弟節點S是紅色節點
或者
解決方案:把P染成紅色,S染成黑色,而後以P爲軸作相應的旋轉操做(D爲P的左子樹則左旋,不然右旋),變成了狀況2(父節點爲紅色),按照狀況2進行操做。
4)節點D的遠親侄子爲紅色節點的狀況(父節點P可紅可黑)
解決方案:交換P和S的顏色,而後把遠親侄子節點SR/SL設置爲黑色,再已P爲軸作相應的旋轉操做(D爲P的左子樹則左旋,不然右旋),刪除節點D。
5)節點D的近親侄子爲紅色節點的狀況(父節點P可紅可黑)
解決方案:把S染成紅色,把近親侄子節點SR/SL染成黑色,而後以節點S爲軸作相應的旋轉操做(D爲P的左子樹則右旋,不然左旋),變成了狀況4,按照狀況4進行操做。
6)節點D,P,S均爲黑色節點
解決方案:把D刪去,而後把節點S染成紅色。
①從節點P往上依然是全黑的狀況
②從節點P往上是其餘狀況
參考博客1:https://segmentfault.com/a/1190000012728513
參考博客2:https://www.cnblogs.com/yinbiao/p/10732600.html
參考博客3:https://www.cnblogs.com/GNLin0820/p/9668378.html
參考博客4:https://blog.csdn.net/tanrui519521/article/details/80980135