平衡二叉樹各種演算法詳解一:紅黑樹
平衡二叉樹(Balanced Binary Tree)具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。平衡二叉樹的常用演算法有紅黑樹、AVL、Treap、伸展樹、SBT等。最小二叉平衡樹的節點的公式如下 F(n)=F(n-1)+F(n-2)+1 這個類似於一個遞迴的數列,可以參考Fibonacci數列,1是根節點,F(n-1)是左子樹的節點數量,F(n-2)是右子樹的節點數量。平衡二叉樹的時間複雜度為o(h),h為樹高,保持樹矮胖的形態有利於提高演算法的效率。
平衡二叉樹中常見操作有插入,刪除,旋轉等。
旋轉是維持平衡二叉樹必要的操作,包括左旋和右旋,二者成映象對稱。
左旋演算法如下(T.ROOT表示樹根,NIL表示空結點)
def Left-Rotate(x): y=x.right if(y.left!=NIL): y.left.parent=x x.right=y.left if(x.parent==NIL): T.ROOT=y else if(x.parent.left==x): x.parent.left=y else x.parent.right=y y.parent=x.parent x.parent=y y.left=x
右旋與之類似,故不再貼出程式碼。
插入演算法如下:
def Tree-Insert(T,z): y=NIL //use y as the parent of x x=T.ROOT while(x!=NIL): y=x if(z.value<x.value): x=x.left else x=x.right z.parent=y if(y==NIL): //tree is empty T.ROOT=z else if(z.value<y.value): y.left=z else y.right=z
刪除演算法需要考慮三種情況:
1. z沒有子結點,直接刪除,修改z的父結點,再用NIL替代z即可
2. z只有一個子結點,將其提升到z的位置,修改z的父結點,再用z的子結點代替z
3. z有兩個子結點,這時稍麻煩一點,需要尋找z的後繼,即z的右子樹中最小的一個結點,還要考慮後繼正好為z的右孩子的情況
首先定義一個子樹的移植演算法:
def Transplant(T,u,v): //用以v為根的子樹替代以U為根的子樹
if(u.parent==NIL)
T.ROOT=v
else if(u==u.parent.left)
u.parent.left=v
else u.parent.right=v
if(v!=NIL)
v.parent=u.parent
再定義一個尋找後繼的方法:
def Tree-min(z):
if(z.left!=NIL)
z=z.left
return z
利用現成的移植演算法進行刪除:
def Tree-delete(T,z)
if(z.left==NIL)
Transplant(T,z,z.right)
else if(z.right==NIL)
Transplant(T,z,z.left)
else y=Tree-min(z.right) //y is the successor of z
if(y.parent!=z)
Transplant(T,y,y.right)
y.right=z.right
y.right.parent=y
Transplant(T,z,y)
z.left.parent=y
y.left=z.left
這就是平衡二叉樹中常見的旋轉,插入,刪除演算法。由於插入,刪除有可能改變樹的平衡性,所以需呼叫旋轉方法以保持樹的平衡性。
下面舉例詳解各種平衡二叉樹。
紅黑樹:
紅黑樹(Red Black Tree) 是一種自平衡二叉查詢樹,是在計算機科學中用到的一種資料結構,典型的用途是實現關聯陣列。紅黑樹在很多地方都有應用。在C++ STL中,很多部分(包括set, multiset, map, multimap)應用了紅黑樹的變體(SGI STL中的紅黑樹有一些變化,這些修改提供了更好的效能,以及對set操作的支援)。
紅黑樹是每個節點都帶有顏色屬性的二叉查詢樹,顏色或紅色或黑色。在二叉查詢樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求: 性質1. 節點是紅色或黑色。 性質2. 根節點是黑色。 性質3 每個葉節點(NIL節點,空節點)是黑色的。 性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點) 性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。 這些約束強制了紅黑樹的關鍵性質: 從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。結果是這個樹大致上是平衡的。因為操作比如插入、刪除和查詢某個值的最壞情況時間都要求與樹的高度成比例,這個在高度上的理論上限允許紅黑樹在最壞情況下都是高效的,而不同於普通的二叉查詢樹。紅黑樹的插入:
def RB-Insert(T,z):
y=T.nil //use y as the parent of x
x=T.ROOT
while(x!=T.nil):
y=x
if(z.value<x.value):
x=x.left
else x=x.right
z.parent=y
if(y==T.nil): //tree is empty
T.ROOT=z
else if(z.value<y.value):
y.left=z
else y.right=z
z.left=T.nil
z.right=T.nil
z.color=RED //z is red
RB-Insert-fixup(T,z) //make the tree follow red-black rule
插入後修復樹的顏色:
def RB-Insert-fixup(T,z)
while(z.parent.color==RED)
if(z.parent==z.parent.parent.left)
y=z.parent.parent.right //y is the uncle node of z
if(y.color==RED) //case 1
z.p.color=BLACK
y.color=BLACK
z.parent.parent.color=RED
z=z.parent.parent
else if(z==z.parent.right) //case 2
z=z.parent
Left-Rotate(T,z)
z.parent.color=BLACK //case 3
z.parent.parent.color=RED
Right-Rotate(T,z.parent.parent) <pre name="code" class="python"> else (same as "then" clause with right and left exchanged) //if a.parent==z.parent.parent.left,case 1,2,3with right and left exchanged
T.ROOT.color=BLACK
紅黑樹的刪除:
同樣先定義一個移植演算法:
def RB-Transplant(T,u,v): //用以v為根的子樹替代以U為根的子樹
if(u.parent==T.nil)
T.ROOT=v
else if(u==u.parent.left)
u.parent.left=v
else u.parent.right=v
v.parent=u.parent
刪除演算法:
def RB-delete(T,z)
y=z
y-original-color=y.color
if(z.left==T.nil)
x=z.right
RB-Transplant(T,z,z.right)
else if(z.right==T.nil)
x=z.left
RB-Transplant(T,z,z.left)
else y=Tree-min(z.right)
y-original-color=y.color
x=y.right
if(y.parent==z)
x.parent=y
else RB-Transplant(T,y,y.right)
y.right=z.right
y.right.parent=y
RB-Transplant(T,z,y)
y.left=z.left
y.left.parent=y
y.color=z.color
if(y-original-color==BLACK)
RB-delete-fixup(T,x)
刪除後的修復顏色屬性操作
RB-delete-fixup(T,x):
def RB-delete-fixup(T,x)
while(x!=T.ROOT&&x.color==BLACK)
if(x==x.parent.left)
w=x.parent.right
if(w.color==RED) //case 1
w.color=BlACK
x.parent.color=RED
Left-Rotate(T,x.parent)
w=x.parent.right
if(w.left.color==BLACK&&w.right.color==BLACK) //case 2
w.color=RED
x=x.parent
else if(w.right.color==BLACK) //case 3
w.left.color=BLACK
w.color=RED
Right-Rotate(T,w)
w=x.parent.right
w.color=x.parent.color //case 4
x.parent.color=BLACK
w.right.color=BLACK
Left-Rotate(T,x.parent)
x=T.ROOT
else (same as "then" clause with right and left exchanged)
x.color=BLACK
(ps:紅黑樹這塊真是要把我弄暈了)