1. 程式人生 > >【資料結構】資料結構探索(四)—— 紅黑樹(R-B Tree)

【資料結構】資料結構探索(四)—— 紅黑樹(R-B Tree)

紅黑樹,一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。

紅黑樹有五個性質:
    性質1. 節點是紅色或黑色。
    性質2. 根節點是黑色。
    性質3 每個葉節點(NIL節點,空節點)是黑色的。
    性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
    性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。

這五個性質對紅黑樹進行了約束,使它從根節點到最遠的葉子節點的路徑長與到最近的葉子節點的路徑長度相差不會超過2。因此它是近似平衡的(並不是絕對平衡的)。

紅黑樹的插入

為了保證紅黑樹並不會整個全是黑色節點,我們都要預設初始的插入節點為紅色,先讓我們來看一看插入節點的虛擬碼:

RB-INSERT(T, z)
y ← nil
x ← T.root
while x ≠ T.nil
	do y ← x
	if z.key < x.key
		then x ← x.left
	else x ← x.right
z.p ← y
if y == nil[T]
	then T.root ← z
else if z.key < y.key
	then y.left ← z
else y.right ← z
z.left ← T.nil
z.right ← T.nil
z.color ← RED
RB-INSERT-FIXUP(T, z)

可以看到我們先找到要插入的父節點,然後按照要插入的節點與父節點的比較結果,將節點插在左子節點或者右子節點,然後為該節點賦予兩個空子節點,並把改節點染成紅色(如上所述),之後在對整棵樹進行插入後的修復。

實際上當父節點是黑色節點的時候,我們無論如何插入一個紅色節點都是不會出現違背規則的情況,所以不用進行修復。

而當父親節點為紅色的時候我們需要考慮三個情況:

● 插入修復情況1:如果當前結點的父結點是紅色且祖父結點的另一個子結點(叔叔結點)是紅色

● 插入修復情況2:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的右子

● 插入修復情況3:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的左子

讓我們來看一下程式碼對這種情況是怎麼處理的吧。

RB-INSERT-FIXUP(T, z)
while z.p.color == RED
	do if z.p == z.p.p.left
		then y ← z.p.p.right
		if y.color == RED
			then z.p.color ← BLACK               ▹ Case 1
			y.color ← BLACK                    ▹ Case 1
			z.p.p.color ← RED                    ▹ Case 1
			z ← z.p.p                            ▹ Case 1
		else if z == z.p.right
			then z ← z.p                          ▹ Case 2
			LEFT-ROTATE(T, z)                   ▹ Case 2
		z.p.color ← BLACK                        ▹ Case 3
		z.p.p.color ← RED                         ▹ Case 3
		RIGHT-ROTATE(T, z.p.p)                  ▹ Case 3
	else (same as then clause with "right" and "left" exchanged)
T.root.color ← BLACK

如果噹噹前節點的父節點為紅色:

情況1:如果當前結點的父結點是紅色且祖父結點的另一個子結點(叔叔結點)是紅色

將父節點的顏色改成黑色,叔叔節點的顏色也改成黑色,將祖父節點的顏色改成紅色。最後,將當前節點指向祖父節點,重複一次判斷,祖父節點的父節點是否為紅色,如果為紅色屬於需要修復的情況幾,再重複執行程式碼。

如圖我們插入了11節點,經過第一次修復,樹變成了這樣:

此時當前節點轉移到了祖父節點15,此時15的父節點為紅色,叔叔節點為黑色,並且他為25的左子,此時應當執行情況3的策略,我們等會再講。

情況2:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的右子

當前節點的父節點做為新的當前節點,以新當前節點為支點左旋。

在10節點上插入右子15節點,此時10的兄弟節點是空節點,空節點一定是黑色的,即叔叔節點為黑色。符合情況2,應當將當前節點變為10節點,然後左旋:

此時當前節點為10,10是15的左子,且叔叔節點為黑色,此時應該有情況3。

情況3:當前節點的父節點是紅色,叔叔節點是黑色,當前節點是其父節點的左子

父節點變為黑色,祖父節點變為紅色,在祖父節點為支點右旋。

我們從上面的圖已經發現修復好情況2以後,場景轉化為情況3,那麼此時現將15變為黑色,將20變成紅色,然後以20為支點右旋轉。

旋轉後25的左子變成了15(黑),而15(黑)有兩個子節點10和20(都為紅色)。如下圖:

這棵樹已經修復完成,因為當前節點為10的父節點15已經為黑色,不需要繼續修復了。

紅黑樹的刪除

"我們刪除的節點的方法與常規二叉搜尋樹中刪除節點的方法是一樣的,如果被刪除的節點不是有雙非空子女,則直接刪除這個節點,用它的唯一子節點頂替它的位置,如果它的子節點分是空節點,那就用空節點頂替它的位置,如果它的雙子全為非空,我們就把它的直接後繼節點內容複製到它的位置,之後以同樣的方式刪除它的後繼節點,它的後繼節點不可能是雙子非空,因此此傳遞過程最多隻進行一次。”

二叉樹的刪除方法我們已經提過了,這裡稍微總結一下:

  1. 沒有兒子,即為葉結點。直接把父結點的對應兒子指標設為NULL,刪除兒子結點就OK了。
  2. 只有一個兒子。那麼把父結點的相應兒子指標指向兒子的獨生子,刪除兒子結點也OK了。
  3. 有兩個兒子。這是最麻煩的情況,因為你刪除節點之後,還要保證滿足搜尋二叉樹的結構。其實也比較容易,我們可以選擇左兒子中的最大元素或者右兒子中的最小元素放到待刪除節點的位置,就可以保證結構的不變。當然,你要記得調整子樹,畢竟又出現了節點刪除。習慣上大家選擇左兒子中的最大元素,其實選擇右兒子的最小元素也一樣,沒有任何差別,只是人們習慣從左向右。這裡咱們也選擇左兒子的最大元素,將它放到待刪結點的位置。左兒子的最大元素其實很好找,只要順著左兒子不斷的去搜索右子樹就可以了,直到找到一個沒有右子樹的結點。那就是最大的了。


然後我們直接來看紅黑樹刪除的虛擬碼:

 1 if left[z] = nil[T] or right[z] = nil[T]  
 2    then y ← z  
 3    else y ← TREE-SUCCESSOR(z)  
 4 if left[y] ≠ nil[T]  
 5    then x ← left[y]  
 6    else x ← right[y]  
 7 p[x] ← p[y]  
 8 if p[y] = nil[T]  
 9    then root[T] ← x  
10    else if y = left[p[y]]  
11            then left[p[y]] ← x  
12            else right[p[y]] ← x  
13 if y ≠ z  
14    then key[z] ← key[y]  
15         copy y's satellite data into z  
16 if color[y] = BLACK  
17    then RB-DELETE-FIXUP(T, x)  
18 return y  

1. 如果被刪除節點的左子為空或者右子為空(兩個子節點至少有一個為空),就先把當前節點地址賦值給y。否則(兩個節點都不為空的情況),對z進行處理TREE-SUCCESSOR(z)後的地址賦值給y。(具體怎麼處理的並不知道。。)。

2. 如果是左子節點不為空,就將左子的父節點指向被刪除的節點的父節點(即左子的祖父節點),如果左子為空,右子不為空,則將右子的父節點指向被刪除的節點的父節點(即右子的祖父節點)。即如果有左子,則將左子設為刪除節點的替換節點,如果沒有左子,那麼將右節點設為替換節點。

3. 如果被刪除節點的父節點為空,則當前被刪除的節點為根節點,那麼將替換節點設為根節點。否則,如果當被刪除的節點是左子,則將祖父節點的左子設為替換節點,如果當前被刪除節點為右子,那麼就將祖父節點的右子指向替換節點。其實到這步基本是上就已經

4. 如果被刪除節點有兩個子節點,那麼把y的關鍵字賦值給當前節點(這步不知道做了什麼事情,有了解的親可以教我)

5. 如被刪除的節點顏色是黑色的,那麼需要進行修復。

上面的部分基本上就是二叉樹的刪除,只是在刪除的節點為黑節點時需要對紅黑樹進行修復,因為如果刪除是紅節點並不會影響紅黑樹規則。

“在刪除節點後,原紅黑樹的性質可能被改變,如果刪除的是紅色節點,那麼原紅黑樹的性質依舊保持,此時不用做修正操作,如果刪除的節點是黑色節點,原紅黑樹的性質可能會被改變,我們要對其做修正操作。那麼哪些樹的性質會發生變化呢,如果刪除節點不是樹唯一節點,那麼刪除節點的那一個支的到各葉節點的黑色節點數會發生變化,此時性質5被破壞。如果被刪節點的唯一非空子節點是紅色,而被刪節點的父節點也是紅色,那麼性質4被破壞。如果被刪節點是根節點,而它的唯一非空子節點是紅色,則刪除後新根節點將變成紅色,違背性質2。!

那麼我們來看修復程式碼:

 1 while x ≠ root[T] and color[x] = BLACK  
 2     do if x = left[p[x]]  
 3           then w ← right[p[x]]  
 4                if color[w] = RED  
 5                   then color[w] ← BLACK                        ▹  Case 1  
 6                        color[p[x]] ← RED                       ▹  Case 1  
 7                        LEFT-ROTATE(T, p[x])                    ▹  Case 1  
 8                        w ← right[p[x]]                         ▹  Case 1  
 9                if color[left[w]] = BLACK and color[right[w]] = BLACK  
10                   then color[w] ← RED                          ▹  Case 2  
11                        x ← p[x]                                ▹  Case 2  
12                   else if color[right[w]] = BLACK  
13                           then color[left[w]] ← BLACK          ▹  Case 3  
14                                color[w] ← RED                  ▹  Case 3  
15                                RIGHT-ROTATE(T, w)              ▹  Case 3  
16                                w ← right[p[x]]                 ▹  Case 3  
17                         color[w] ← color[p[x]]                 ▹  Case 4  
18                         color[p[x]] ← BLACK                    ▹  Case 4  
19                         color[right[w]] ← BLACK                ▹  Case 4  
20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4  
21                         x ← root[T]                            ▹  Case 4  
22        else (same as then clause with "right" and "left" exchanged)  
23 color[x] ← BLACK  

“上面的修復情況看起來有些複雜,下面我們用一個分析技巧:我們從被刪節點後來頂替它的那個節點開始調整,並認為它有額外的一重黑色。這裡額外一重黑色是什麼意思呢,我們不是把紅黑樹的節點加上除紅與黑的另一種顏色,這裡只是一種假設,我們認為我們當前指向它,因此空有額外一種黑色,可以認為它的黑色是從它的父節點被刪除後繼承給它的,它現在可以容納兩種顏色,如果它原來是紅色,那麼現在是紅+黑,如果原來是黑色,那麼它現在的顏色是黑+黑。有了這重額外的黑色,原紅黑樹性質5就能保持不變。現在只要恢復其它性質就可以了,做法還是儘量向根移動和窮舉所有可能性。"--saturnman。

從程式碼中可以看到,如果刪除的不是根節點,且節點顏色為黑色的時候,分兩種大情況:

1. 如果是以下情況,恢復比較簡單:

  • a)當前節點是紅+黑色
    解法,直接把當前節點染成黑色,結束此時紅黑樹性質全部恢復。如下:

途中有兩種情況符合 此要求

a. 刪除5節點.此時兄弟節點為紅色,刪除5節點後,8節點頂上,然後8節點變為黑色。變化結束。

b. 為刪除18節點,此時會紅+黑,兄弟節點為紅色,但是處理方法應該沒什麼不同

如圖所示確實並沒有不同之處。

  • b)當前節點是黑+黑且是根節點, 解法:什麼都不做,結束

如我們刪除20節點,即把左子樹最左邊的資料提到根節點:

2. 比較複雜的是以下4種:

刪除修復情況1:當前節點是黑+黑且兄弟節點為紅色(此時父節點和兄弟節點的子節點分為黑)
刪除修復情況2:當前節點是黑加黑且兄弟是黑色且兄弟節點的兩個子節點全為黑色
刪除修復情況3:當前節點顏色是黑+黑,兄弟節點是黑色,兄弟的左子是紅色,右子是黑色
刪除修復情況4:當前節點顏色是黑-黑色,它的兄弟節點是黑色,但是兄弟節點的右子是紅色,兄弟節點左子的顏色任意

讓我們來具體看一下虛擬碼,從程式碼中可以看出,當x不為根節點且顏色為黑時會進入修復迴圈(此時是已被刪除節點已被替換節點替換,當前節點x實際上指的是替換節點)。

刪除修復情況1:當前節點是黑+黑且兄弟節點為紅色(此時父節點和兄弟節點的子節點分為黑)

解法:把父節點染成紅色,把兄弟結點染成黑色,之後重新進入演算法(我們只討論當前節點是其父節點左孩子時的情況)。此變換後原紅黑樹性質5不變,而把問題轉化為兄弟節點為黑色的情況(注:變化前,原本就未違反性質5,只是為了把問題轉化為兄弟節點為黑色的情況)。 即如下圖操作,當A為替換節點(我自己無法模擬,使用了別人的圖):

變化後:

刪除修復情況2:當前節點是黑加黑且兄弟是黑色且兄弟節點的兩個子節點全為黑色。

解法:把當前節點和兄弟節點中抽取一重黑色追加到父節點上,把父節點當成新的當前節點,重新進入演算法。(此變換後性質5不變),即呼叫RB-INSERT-FIXUP(T, z) 的第9-10行程式碼操作,如下:

 if color[left[w]] = BLACK and color[right[w]] = BLACK  
10                   then color[w] ← RED                          ▹  Case 2  
11                        x ← p[x]                                ▹  Case 2 

變換後:

刪除修復情況3:當前節點顏色是黑+黑,兄弟節點是黑色,兄弟的左子是紅色,右子是黑色。

解法:把兄弟結點染紅,兄弟左子節點染黑,之後再在兄弟節點為支點解右旋,之後重新進入演算法。此是把當前的情況轉化為情況4,而性質5得以保持,即呼叫RB-INSERT-FIXUP(T, z) 的第12-16行程式碼,如下所示:

12                   else if color[right[w]] = BLACK  
13                           then color[left[w]] ← BLACK          ▹  Case 3  
14                                color[w] ← RED                  ▹  Case 3  
15                                RIGHT-ROTATE(T, w)              ▹  Case 3  
16                                w ← right[p[x]]                 ▹  Case 3  

變化前:

變化後:

刪除修復情況4:當前節點顏色是黑-黑色,它的兄弟節點是黑色,但是兄弟節點的右子是紅色,兄弟節點左子的顏色任意。

解法:把兄弟節點染成當前節點父節點的顏色,把當前節點父節點染成黑色,兄弟節點右子染成黑色,之後以當前節點的父節點為支點進行左旋,此時演算法結束,紅黑樹所有性質調整正確,即呼叫RB-INSERT-FIXUP(T, z)的第17-21行程式碼,如下所示:
 

//呼叫RB-DELETE-FIXUP(T, x) 的第17-21行程式碼
17                         color[w] ← color[p[x]]                 ▹  Case 4
18                         color[p[x]] ← BLACK                    ▹  Case 4
19                         color[right[w]] ← BLACK                ▹  Case 4
20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4
21                         x ← root[T]                            ▹  Case 4

變化前:

變化後:

相關推薦

資料結構資料結構探索—— R-B Tree

紅黑樹,一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。 紅黑樹有五個性質:     性質1. 節點是紅色或黑色。     性質2. 根節點是黑色。     性質3 每個葉節點(NIL節點,空節點)是黑色的。     性質4 每

數據結構--- RedBlock-Tree

rabl real-time 消息 ren linu 轉變 數據結構 replace 算法 文章圖片來自鄧俊輝老師課件 先提幾個問題去思考學習本文 : 紅黑樹和2-4樹(B-Tree)很像,那麽它存在的動機又是什麽呢 插入和刪

漫畫演算法:什麼是適合初學小白簡單易懂

———————————— 二叉查詢樹(BST)具備什麼特性呢? 1.左子樹上所有結點的值均小於或等於它的根結點的值。 2.右子樹上所有結點的值均大於或等於它的根結點的值。 3.左、右子樹也分別為二叉排序樹。 下

二叉查詢BST | 平衡二叉查詢AVL | RBT

二叉查詢樹(BST) 特點:對任意節點而言,左子樹(若存在)的值總是小於本身,而右子(若存在)的值總是大於本身。 查詢:從根開始,小的往左找,大的往右找,不大不小的就是這個節點了; 插入:從根開始,小的往左,大的往右,直到葉子,就插入, 時間複雜度期望為

查詢演算法6RBT

前言:        2-3樹雖然能實現平衡性,但在插入和刪除的過程中需要判斷插入的節點是2-節點還是3-節點等一系列問題,實現複雜且會增加額外的開銷,所以就提出了紅黑樹(發明者--Sedgewick,1987年)一.基本概念 1. R-B Tree,全稱是Red-Black

資料結構如何實現及怎樣判斷

      紅黑樹是一顆二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是red或black。通過對任何一條從根節點到葉子節點的簡單路徑上的顏色來約束,紅黑樹保證了最長路徑不超過最短路經的兩倍,因此近似於平衡。 紅黑樹的規則: 1、每個節點不是紅色就是

資料結構學習筆記------附c++程式碼

1、紅黑樹簡介 紅黑樹是二叉查詢樹的一種,其增刪改查的統計效能要優於AVL樹,查詢、插入、刪除演算法的複雜度都為O(log(n))。先附上紅黑樹這種資料結構的性質: 性質1、節點是紅色或黑色。 性質2、根節點是黑色。 性質3、每個葉節點(是指的空節點,nil節點)是黑色的。 性質4、

資料結構與演算法:Red Black Tree

一、簡介 紅黑樹(Red Black Tree)是一棵二叉查詢樹,在每個節點增加一個屬性表示節點顏色,值為紅色(Red)或者黑色(Black)。紅黑樹也是“平衡”樹中的一種,通過對任何一條從根到葉子的路徑上各個節點的顏色來進行約束,確保沒有一條路徑會比其他

linux核心分析--核心中的資料結構

#include<linux/rbtree.h> #include <linux/string.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); struct student { int id;

linux核心分析--核心中的資料結構

紅黑樹由於節點顏色的特性,保證其是一種自平衡的二叉搜尋樹。 紅黑樹的一系列規則雖然實現起來比較複雜,但是遵循起來卻比較簡單,而且紅黑樹的插入,刪除效能也還不錯。 所以紅黑樹在核心中的應用非常廣泛,掌握好紅黑樹,即有利於閱讀核心原始碼,也可以在自己的程式碼中借鑑這種資料結構。 紅黑樹必

資料結構——插入操作

插入或刪除操作,都有可能改變紅黑樹的平衡性,利用顏色變化與旋轉這兩大法寶就可應對所有情況,將不平衡的紅黑樹變為平衡的紅黑樹。 在進行顏色變化或旋轉的時候,往往要涉及祖孫三代節點:X表示操作的基準節點,P代表X的父節點,G代表X的父節點的父節點。 我們先來大體預覽一下插入的

資料結構——red-black tree

RB-tree(紅黑樹)是一種平衡二叉搜尋樹,它每個節點上增加了一個儲存位來表示節點的顏色,可以是 Red 或 Black,故得名。通過對任何一條從根到葉子的簡單路徑上各個節點的顏色進行約束,紅黑樹能夠確保沒有一條路徑會比其他路徑長出 2 倍,近似於平衡。

資料結構---

一、簡述 紅黑樹是一種特殊的二叉樹,並且是優秀的自平衡查詢樹,下圖為紅黑樹的示例: 紅黑樹具有以下幾大特性: 1、根節點為黑色。 2、所有節點都是黑色或紅色。 3、所有葉子節點(Null)都是黑色。 4、紅色節點的子節點一定是黑色的。 5、任意一個節點到其葉子節點的所有路徑上的黑色節點數量相同(黑色完美平

數據結構Java版之

如何 當前 鏈接 根節點 java版 -- 查找 變色 繼承   紅黑樹是一種自動平衡的二叉查找樹,因為存在紅黑規則,所以有效的防止了二叉樹退化成了鏈表,且查找和刪除的速度都很快,時間復雜度為log(n)。   什麽是紅黑規則?   1.根節點必須是黑色的。   2.節點顏

數據結構 - Red Black Tree插入詳解與實現Java

啟示 dpa con 技術分享 節點數 src 通知 一點 this   最終還是決定把紅黑樹的篇章一分為二,插入操作一篇,刪除操作一篇,因為合在一起寫篇幅實在太長了,寫起來都覺得累,何況是閱讀並理解的讀者。       紅黑樹刪除操作請參考 數據結構 - 紅黑樹(Red

數據結構 - Red Black Tree刪除詳解與實現Java

replace ati 轉載 之前 9.png one com 四種 簡單   本篇要講的就是紅黑樹的刪除操作       紅黑樹插入操作請參考 數據結構 - 紅黑樹(Red Black Tree)插入詳解與實現(Java)   紅黑樹的刪除是紅黑樹操作中比較麻煩且比較有意

演算法二叉概念與查詢

誒,演算法這個東西,其實沒那麼簡單,但是也沒那麼難。 紅黑樹,其實已經有很多大佬都整理過了,而且文章部落格都寫得超好,我寫這篇文章的目的是:自己整理一次,這些知識才是自己的,否則永遠是別人的~   該系列到現在暫只有3篇文章: 【演算法】紅黑樹(二叉樹)概念與查詢(一):h

演算法手撕—— 基本性質以及插入實現附帶程式碼實現

在閱讀其他博主關於紅黑樹增刪實現的時候,博主們大多直接使用文字圖片描述,對整個增刪整體的流程突出的不太明顯(當然dalao們寫得還是很棒得,不然我也寫不出這篇文章),所以我特意花了2天時間用CAD製作了 一張插入操作的流程圖和一張刪除操作的流程圖(刪除見下篇)並手撕程式碼(好吧,其實大部分時間在除錯程式碼,畢

2-3 /red-black tree

https ret html 技術分享 turn nfc font 進行 sre 2-3 tree 2-3樹節點: null節點,null節點到根節點的距離都是相同的,所以2-3數是平衡樹 2叉節點,有兩個分樹,節點中有一個元素,左樹元素更小,右樹元素節點更大 3叉節點

Red Black Tree刪除詳解與實現Java

  本篇要講的就是紅黑樹的刪除操作   紅黑樹的刪除是紅黑樹操作中比較麻煩且比較有意思的一部分。   在此之前,重申一遍紅黑樹的五個定義: 1. 紅黑樹的節點要不是黑色的要不是紅色的     2. 紅黑樹的根節點一定是黑色的     3. 紅黑樹的所有葉子節點都是黑色的(注意:紅黑樹的葉子節點指Nil節點