紅黑樹
紅黑樹概念
紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
紅黑樹的性質
- 每個結點不是紅色就是黑色
- 根節點是黑色的
- 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
- 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
- 每個葉子結點都是黑色的(此處的葉子結點指的是空結點)
爲什麼滿足上面的性質,紅黑樹就能保證:其最長路徑中節點個數不會超過最短路徑節點個數的兩倍?
答案:最短的路徑上節點的顏色全部都爲黑色;最長的路徑則爲黑紅交叉的路徑,其上有與最短路徑的黑節點數目相同的黑節點數和紅節點數目。所以我們按照紅黑樹性質所建立的紅黑樹的最長路徑必然不會超過最短路徑的兩倍!
紅黑樹節點的定義
class RBTreeNode{
RBTreeNode left = null;
RBTreeNode right = null;
RBTreeNode parent = null;
COLOR color = RED; // 節點的顏色
int val;
public RBTreeNode(int val){
this.val = val;
}
}
在節點的定義中,爲什麼要將節點的默認顏色給成紅色的?
插入黑節點必然會影響所有路徑都含有相同數目的黑色節點這一原則,較難維護。
紅黑樹的插入(重點)
紅黑樹是在二叉搜索樹的基礎上加上其平衡限制條件,因此紅黑樹的插入可分爲兩步:
1. 按照二叉搜索的樹規則插入新節點
2. 檢測新節點插入後,紅黑樹的性質是否造到破壞
因爲新節點的默認顏色是紅色,因此:如果其雙親節點的顏色是黑色,沒有違反紅黑樹任何性質,則不需要調整;但當新插入節點的雙親節點顏色爲紅色時,就違反了性質三不能有連在一起的紅色節點,此時需要對紅黑樹分情況來討論:
約定:cur爲當前節點,p爲父節點,g爲祖父節點,u爲叔叔節點
情況一: cur爲紅,p爲紅,g爲黑,u存在且爲紅
cur和p均爲紅,違反了性質三,此處能否將p直接改爲黑?
解決方式:將p,u改爲黑,g改爲紅,然後把g當成cur,繼續向上調整。
情況二: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
p爲g的左孩子,cur爲p的左孩子,則進行右單旋轉;相反,
p爲g的右孩子,cur爲p的右孩子,則進行左單旋轉
p、g變色–p變黑,g變紅
情況三: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
p爲g的左孩子,cur爲p的右孩子,則針對p做左單旋轉;相反,
p爲g的右孩子,cur爲p的左孩子,則針對p做右單旋轉
則轉換成了情況2
代碼如下:
public boolean insert(int val){
// ...
// 新節點插入後,如果parent節點的顏色是紅色,一定違反性質三
while(null != parent && COLOR.RED == parent.color){
RBTreeNode grandFather = parent.parent;
if(parent == grandFather.left){
RBTreeNode uncle = grandFather.right;
if(null != uncle && uncle.color == COLOR.RED){
// 情況一:叔叔節點存在且爲紅,
// 解決方式:將叔叔和雙親節點改爲黑色,祖父節點改爲紅色// 如果祖父的雙親節點的顏色是紅色,需要繼續往上調整
parent.color=COLOR.BLACK;
uncle.color=COLOR.BLACK;
grandFather.color=COLOR.RED;
cur=grandFather;
parent=cur.parent;
}
else
{
// 情況二和情況三
// 叔叔節點不存在 || 叔叔節點存在,但是顏色是黑色
if(cur==parent.right)
{
// 情況三
rotateLeft(parent);
RBTreeNode temp=parent;
parent=cur;
cur=temp;
}
// 情況二
parent.color=COLOR.BLACK;
grandFather.color=COLOR.RED;
rotateRight(grandFather);
}
}
else{
// 課件圖解的反情況,即叔叔節點在左側
// 此處,請同學們自行處理
}
}
// 在上述循環更新期間,可能會將根節點給成紅色而違反性質1,因此此處必須將根節點改爲黑色
root.color=COLOR.BLACK;
return true;
}
AVL樹和紅黑樹的比較
紅黑樹和AVL樹都是高效的平衡二叉樹,增刪改查的時間複雜度都是O(),紅黑樹不追求絕對平衡,其只需保證最長路徑不超過最短路徑的2倍,相對而言,降低了插入和旋轉的次數,所以在經常進行增刪的結構中性能比AVL樹更優,而且紅黑樹實現比較簡單,所以實際運用中紅黑樹更多。
紅黑樹應用
java集合框架j中的:TreeMap、TreeSet底層使用的就是紅黑樹