紅黑樹(red-black tree,簡稱rb樹),是一種特殊的二叉查詢樹,所以他也滿足二叉查詢樹的特徵:任意乙個節點的值大於右子節點的鍵值,小於左子節點的鍵值。除此之外,紅黑樹還具備很多其他特徵:
1)每個節點都是紅色或者黑色
2)根節點必定為黑色
3)每個葉節點(左右子節點都為null的節點)必定是黑色
4)如果乙個節點是紅色的,他的子節點必須是黑色的(反之則不一定),也就是說不允許有兩個連續的紅色節點,但可以有連續的黑色節點。
5)從根節點到葉節點或空節點的每條路徑上,包含的黑色節點數相同,即黑色高度相同。
注意:執行插入操作時的新節點總是紅色的,因為插入乙個紅色節點比插入乙個黑色節點違反紅黑樹規則的可能性更小,如果插入的是黑色節點那必然將改變黑色高度,需要進行自修正,而插入乙個紅色節點,只有一半的可能遇到出現連續兩個紅色節點的情況。
紅黑樹示意圖:
紅黑樹主要有三種方式對平衡進行修正:重新著色,左旋,右旋。
1)重新著色
新插入的節點為紅色節點15,插入後有兩個連續的紅色節點,這時我們將其父節點及其兄弟節點重新著色成黑色,這樣既保證了沒有連續的紅色節點也保證了黑色高度的統一。
2)右旋
如果對乙個節點進行右旋,這個節點會向右和向下移動到他原來位置的右節點處,他的左節點將移動到他原來的位置,左節點的右子樹將變成他的左子樹。進行右旋的節點必須要有左節點。
3) 左旋
3.左旋右旋的具體實現
1)節點類
節點類和二叉樹的節點類相似,增加了乙個bool值來描述節點的顏色
public
class
rbnode
comparable
>
//獲得節點的關鍵值
public t getkey()
//列印節點的關鍵值和顏色資訊
public string tostring()
}
2)左旋/*************對紅黑樹節點x進行左旋操作 ******************/
/* * 左旋示意圖:對節點x進行左旋
* p p
* / /
* x y
* / \ / \
* lx y -----> x ry
* / \ / \
* ly ry lx ly
* 左旋做了三件事:
* 1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時)
* 2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點為y(左或右)
* 3. 將y的左子節點設為x,將x的父節點設為y
*/private
void
leftrotate
(rbnode
x)//2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點為y(左或右)
y.parent = x.parent;
if(x.parent == null)
else
else
}//3. 將y的左子節點設為x,將x的父節點設為y
y.left = x;
x.parent = y;
}
3) 右旋/*************對紅黑樹節點y進行右旋操作 ******************/
/* * 左旋示意圖:對節點y進行右旋
* p p
* / /
* y x
* / \ / \
* x ry -----> lx y
* / \ / \
* lx rx rx ry
* 右旋做了三件事:
* 1. 將x的右子節點賦給y的左子節點,並將y賦給x右子節點的父節點(x右子節點非空時)
* 2. 將y的父節點p(非空時)賦給x的父節點,同時更新p的子節點為x(左或右)
* 3. 將x的右子節點設為y,將y的父節點設為x
*/private
void
rightrotate
(rbnode
y)//2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點為y(左或右)
x.parent = y.parent;
if(y.parent == null)
else
else
}//3. 將x的左子節點設為y,將y的父節點設為y
x.right = y;
y.parent = x;
}
將乙個節點插入到紅黑樹中,需要執行哪些步驟呢?首先,將紅黑樹當作一顆二叉查詢樹,將節點插入;然後,將節點著色為紅色;最後,通過"旋轉和重新著色"等一系列操作來修正該樹,使之重新成為一顆紅黑樹。詳細描述如下:
第一步: 將紅黑樹當作一顆二叉查詢樹,將節點插入。
紅黑樹本身就是一顆二叉查詢樹,將節點插入後,該樹仍然是一顆二叉查詢樹。也就意味著,樹的鍵值仍然是有序的。此外,無論是左旋還是右旋,若旋轉之前這棵樹是二叉查詢樹,旋轉之後它一定還是二叉查詢樹。這也就意味著,任何的旋轉和重新著色操作,都不會改變它仍然是一顆二叉查詢樹的事實。
好吧?那接下來,我們就來想方設法的旋轉以及重新著色,使這顆樹重新成為紅黑樹!
第二步:將插入的節點著色為"紅色"。
為什麼著色成紅色,而不是黑色呢?為什麼呢?在回答之前,我們需要重新溫習一下紅黑樹的特性:
(1) 每個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每個葉子節點是黑色。 [注意:這裡葉子節點,是指為空的葉子節點!]
(4) 如果乙個節點是紅色的,則它的子節點必須是黑色的。
(5) 從乙個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
將插入的節點著色為紅色,不會違背"特性(5)"!少違背一條特性,就意味著我們需要處理的情況越少。接下來,就要努力的讓這棵樹滿足其它性質即可;滿足了的話,它就又是一顆紅黑樹了。o(∩∩)o…哈哈
第三步: 通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。
第二步中,將插入節點著色為"紅色"之後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
對於"特性(1)",顯然不會違背了。因為我們已經將它塗成紅色了。
對於"特性(2)",顯然也不會違背。在第一步中,我們是將紅黑樹當作二叉查詢樹,然後執行的插入操作。而根據二叉查詢數的特點,插入操作不會改變根節點。所以,根節點仍然是黑色。
對於"特性(3)",顯然不會違背了。這裡的葉子節點是指的空葉子節點,插入非空節點並不會對它們造成影響。
對於"特性(4)",是有可能違背的!
那接下來,想辦法使之"滿足特性(4)",就可以將樹重新構造成紅黑樹了。
上面**完了紅-黑樹的插入操作,接下來討論刪除,紅-黑樹的刪除和二叉查詢樹的刪除是一樣的,只不過刪除後多了個平衡的修復而已。我們先來回憶一下二叉搜尋樹的刪除:
①、如果待刪除的節點沒有子節點,那麼直接刪除即可。
②、如果待刪除的節點只有乙個子節點,那麼直接刪掉,並用其子節點去頂替它。
③、如果待刪除的節點有兩個子節點,這種情況比較複雜:首先找出它的後繼節點,然後處理「後繼節點」和「被刪除節點的父節點」之間的關係,最後處理「後繼節點的子節點」和「被刪除節點的子節點」之間的關係。每一步中也會有不同的情況。
實際上,刪除過程太複雜了,很多情況下會採用在節點類中新增乙個刪除標記,並不是真正的刪除節點。詳細的刪除我們這裡不做討論。
java實現紅黑樹
package com.lsl public class redblacktree 節點左旋方法,將當前節點變為其右子樹的左子樹 private void leftrotate redblacknode x y.parent x.parent 將x的父親變為y的父親 if x.parent null...
紅黑樹的java實現
紅黑樹的原理以及演算法見文章 紅黑樹的節點定義 package org.algorithm.redblacktree created with intellij idea.user qarkly date 14 6 8 time 下午11 16 to change this template use...
紅黑樹下 紅黑樹的實現
1.實現紅黑樹的基本思想 實際上,紅黑樹是有固定的平衡過程的 遇到什麼樣的節點分布,我們就對應怎麼去調整。只要按照這些固定的調整規則來操作,就能將乙個非平衡的紅黑樹調整成平衡的。首先,我們需要再來看一下紅黑樹的定義 在插入 刪除節點的過程中,第 三 四點要求可能會被破壞,所以 平衡調整 實際上就是把...