紅黑樹之 原理和算法詳細介紹(阿里面試-treemap使用了紅黑樹) 紅黑樹的時間複雜度是O(lgn) 高度<=2log(n+1)一、X節點左旋-將X右邊的子節點變成 父節點 二、X節點右旋-將X左邊

紅黑樹插入刪除 具體參考:紅黑樹原理以及插入、刪除算法 附圖例說明   (阿里的高德一直追着問)html

或者插入的狀況參考:紅黑樹原理以及插入、刪除算法 附圖例說明java

紅黑樹與AVL樹 紅黑樹 的時間複雜度 O(logn) TreeMap TreeSet自己就是一個紅黑樹的實現。node

「紅黑樹」,它一種特殊的二叉查找樹。紅黑樹的每一個節點上都有存儲位表示節點的顏色,能夠是紅(Red)或黑(Black)。 

紅黑樹的時間複雜度爲: O(lgn)面試

(1) 一棵含有n個節點的紅黑樹的高度至多爲2log(n+1)  算法

(2) 左旋:本身成爲左子節點將「x的右孩子」設爲「x的父親節點」;數組

(3)右旋:本身成爲右子節點將「x的左孩子」設爲「x的父親節點」;數據結構

紅黑樹可以以O(log n) 的時間複雜度進行搜索、插入、刪除操做。此外,因爲它的設計,任何不平衡都會在三次旋轉以內解決函數

(4)紅黑樹的特性:(阿里-很重要
(1)每一個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每一個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!]
(4)若是一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。 post

1 排序二叉樹 

二叉排序樹,又叫二叉查找樹,它或者是一棵空樹;或者是具備如下性質的二叉樹:
1. 若它的左子樹不空,則左子樹上全部節點的值均小於它的根節點的值;
2. 若它的右子樹不空,則右子樹上全部節點的值均大於它的根節點的值;
3. 它的左右子樹也分別爲二叉排序樹。性能

對排序二叉樹,若按中序遍歷就能夠獲得由小到大的有序序列。 

好比A:根節點、B:左節點、C:右節點,前序順序是ABC(根節點排最早,而後同級先左後右);中序順序是BAC(先左後根最後右);後序順序是BCA(先左後右最後根)

 

 

 

 

排序二叉樹雖然能夠快速檢索,但在最壞的狀況下:若是插入的節點集自己就是有序的,要麼是由小到大排列,要麼是由大到小排列,那麼最後獲得的排序二叉樹將變成鏈表:全部節點只有左節點(若是插入節點集自己是大到小排列);或全部節點只有右節點(若是插入節點集自己是小到大排列)。在這種狀況下,排序二叉樹就變成了普通鏈表,其檢索效率就會不好


AVL樹-自平衡二叉查找樹

是最早發明的自平衡二叉查找樹。 在AVL樹中任何節點的兩個兒子子樹的高度最大差異爲一,因此它也被稱爲高度平衡樹。查找、插入和刪除在平均和最壞狀況下都是O(log n)。增長和刪除可能須要經過一次或屢次樹旋轉來從新平衡這個樹。
引入二叉樹的目的是爲了提升二叉樹的搜索的效率,減小樹的平均搜索長度.爲此,就必須每向二叉樹插入一個結點時調整樹的結構,使得二叉樹搜索保持平衡,從而可能下降樹的高度,減小的平均樹的搜索長度.
 
AVL樹的定義:
一棵AVL樹知足如下的條件:
1>它的左子樹和右子樹都是AVL樹
2>左子樹和右子樹的高度差不能超過1
性質:
1>一棵n個結點的AVL樹的其高度保持在0(log2(n)),不會超過3/2log2(n+1)
2>一棵n個結點的AVL樹的平均搜索長度保持在0(log2(n)).
3>一棵n個結點的AVL樹刪除一個結點作平衡化旋轉所須要的時間爲0(log2(n)).
 
爲了保證平衡,AVL樹中的每一個結點都有一個平衡因子(balance factor,如下用BF表示),它表示這個結點的左、右子樹的高度差,也就是左子樹的高度減去右子樹的高度的結果值。AVL樹上全部結點的BF值只能是-一、0、1。反之,只要二叉樹上一個結點的BF的絕對值大於1,則該二叉樹就不是平衡二叉樹。下圖演示了平衡二叉樹和非平衡二叉樹。

2 紅黑樹

一棵擁有n個內部結點的紅黑樹的樹高h<=2log(n+1)

數據項只能存儲在內部結點中(internal node)。咱們所指的"葉結點"在其父結點中可能僅僅用一個NULL指針表示,可是將它也看做一個實際的結點有助於描述紅黑樹的插入與刪除算法,葉結點一概爲黑色。 定義詳解: 

根據性質 5:紅黑樹從根節點到每一個葉子節點的路徑都包含相同數量的黑色節點,所以從根節點到葉子節點的路徑中包含的黑色節點數被稱爲樹的「黑色高度(black-height)」。

性質 4 則保證了從根節點到葉子節點的最長路徑的長度不會超過任何其餘路徑的兩倍。假若有一棵黑色高度爲 3 的紅黑樹:從根節點到葉節點的最短路徑長度是 2,該路徑上全是黑色節點(黑節點 – 黑節點 – 黑節點)。最長路徑也只可能爲 4,在每一個黑色節點之間插入一個紅色節點(黑節點 – 紅節點 – 黑節點 – 紅節點 – 黑節點),性質 4 保證毫不可能插入更多的紅色節點。因而可知,紅黑樹中最長路徑就是一條紅黑交替的路徑

 

根據定義咱們作以下練習: -不符合定義的一顆非紅黑樹: 紅黑樹的這5個性質中,第3點是比較難理解的,但它卻很是有必要。咱們看圖1中的左邊這張圖,若是不使用黑哨兵,它徹底知足紅黑樹性質,結點50到兩個葉結點8和葉結點82路徑上的黑色結點數都爲2個。但若是加入黑哨兵後(如圖1右圖中的小黑圓點),葉結點的個數變爲8個黑哨兵,根結點50到這8個葉結點路徑上的黑高度就不同了,因此它並非一棵紅黑樹。 -兩顆正確的紅黑樹:  定理

由此咱們能夠得出結論:對於給定的黑色高度爲 N 的紅黑樹,從根到葉子節點的最短路徑長度爲 N-1,最長路徑長度爲 2 * (N-1)。 提示:排序二叉樹的深度直接影響了檢索的性能,正如前面指出,當插入節點自己就是由小到大排列時,排序二叉樹將變成一個鏈表,這種排序二叉樹的檢索性能最低:N 個節點的二叉樹深度就是 N-1。 紅黑樹經過上面這種限制來保證它大體是平衡的——由於紅黑樹的高度不會無限增高,這樣保證紅黑樹在最壞狀況下都是高效的,不會出現普通排序二叉樹的狀況。 因爲紅黑樹只是一個特殊的排序二叉樹,所以對紅黑樹上的只讀操做與普通排序二叉樹上的只讀操做徹底相同,只是紅黑樹保持了大體平衡,所以檢索性能比排序二叉樹要好不少。 但在紅黑樹上進行插入操做和刪除操做會致使樹再也不符合紅黑樹的特徵,所以插入操做和刪除操做都須要進行必定的維護,以保證插入節點、刪除節點後的樹依然是紅黑樹。

3 紅黑樹和AVL(自平衡二叉查找樹)樹的比較

1.  紅黑樹並不追求「徹底平衡」——它只要求部分地達到平衡要求,下降了對旋轉的要求,從而提升了性能。
 
紅黑樹可以以 O(log n) 的時間複雜度進行搜索、插入、刪除操做。此外, 因爲它的設計,任何不平衡都會在三次旋轉以內解決。固然,還有一些更好的,但實現起來更復雜的數據結構,可以作到一步旋轉以內達到平衡,但紅黑樹可以給咱們一個比較「便宜」的解決方案。 紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高
 
固然,紅黑樹並不適應全部應用樹的領域。若是數據基本上是靜態的,那麼讓他們待在他們可以插入,而且不影響平衡的地方會具備更好的性能。若是數據徹底是靜態的,作一個哈希表,性能可能會更好一些。
 
紅黑樹是一個更高效的檢索二叉樹,所以經常用來實現關聯數組。典型地,JDK 提供的集合類 TreeMap 自己就是一個紅黑樹的實現。
 
IBM DevelopWorks 上一篇文章講解很是好,供參考。
 
TreeMap 和 TreeSet 是 Java Collection Framework 的兩個重要成員,其中 TreeMap 是 Map 接口的經常使用實現類,而 TreeSet 是 Set 接口的經常使用實現類。雖然 HashMap 和 HashSet 實現的接口規範不一樣,但 TreeSet 底層是經過 TreeMap 來實現的,所以兩者的實現方式徹底同樣。而 TreeMap 的實現就是紅黑樹算法。
 
對於 TreeMap 而言,因爲它底層採用一棵「紅黑樹」來保存集合中的 Entry,這意味這 TreeMap 添加元素、取出元素的性能都比 HashMap 低:當 TreeMap 添加元素時,須要經過循環找到新增 Entry 的插入位置,所以比較耗性能;當從 TreeMap 中取出元素時,須要經過循環才能找到合適的 Entry,也比較耗性能。
 
但 TreeMap、TreeSet 比 HashMap、HashSet 的優點在於:TreeMap 中的全部 Entry 老是按 key 根據指定排序規則保持有序狀態,TreeSet 中全部元素老是根據指定排序規則保持有序狀態。 
 
從1這點來看紅黑樹是犧牲了嚴格的高度平衡的優越條件爲代價紅黑樹可以以O(log2 n)的時間複雜度進行搜索、插入、刪除操做。 此外,因爲它的設計,任何不平衡都會在三次旋轉以內解決。固然,還有一些更好的,但實現起來更復雜的數據結構可以作到一步旋轉以內達到平衡,但紅黑樹可以給咱們一個比較「便宜」的解決方案。紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高. 

紅黑樹示意圖以下:

 注意

(01) 特性(3)中的葉子節點,是隻爲空(NIL或null)的節點。
(02) 特性(5),確保沒有一條路徑會比其餘路徑長出倆倍。於是,紅黑樹是相對是接近平衡的二叉樹。

 

 

 

紅黑樹的基本操做(一) 左旋和右旋(阿里面試

紅黑樹的基本操做是添加刪除。在對紅黑樹進行添加或刪除以後,都會用到旋轉方法。爲何呢?道理很簡單,添加或刪除紅黑樹中的節點以後,紅黑樹就發生了變化,可能不知足紅黑樹的5條性質,也就再也不是一顆紅黑樹了,而是一顆普通的樹。而經過旋轉,可使這顆樹從新成爲紅黑樹。簡單點說,旋轉的目的是讓樹保持紅黑樹的特性。
旋轉包括兩種:左旋 和 右旋。下面分別對它們進行介紹。

 

1. 左旋

對x進行左旋,意味着"將x變成一個左節點"。 

理解左旋以後,看看下面一個更鮮明的例子。你能夠先不看右邊的結果,本身嘗試一下。

 

2. 右旋

對x進行左旋,意味着"將x變成一個左節點"。 

理解右旋以後,看看下面一個更鮮明的例子。你能夠先不看右邊的結果,本身嘗試一下。


旋轉總結

(01) 左旋 和 右旋 是相對的兩個概念,原理相似。理解一個也就理解了另外一個。

(02) 下面談談如何區分 左旋 和 右旋。
在實際應用中,若沒有完全理解 左旋 和 右旋,可能會將它們混淆。下面談談我對如何區分 左旋 和 右旋 的理解。

 

3. 區分 左旋 和 右旋

仔細觀察上面"左旋"和"右旋"的示意圖。咱們能清晰的發現,它們是對稱的。不管是左旋仍是右旋,被旋轉的樹,在旋轉前是二叉查找樹,而且旋轉以後仍然是一顆二叉查找樹。

 

左旋示例圖(以x爲節點進行左旋):

                               z
   x                          /                  
  / \      --(左旋)-->       x
 y   z                      /
                           y

對x進行左旋,意味着,將「x的右孩子」設爲「x的父親節點」;即,將 x變成了一個左節點(x成了爲z的左孩子)!。 所以,左旋中的「左」,意味着「被旋轉的節點將變成一個左節點」

右旋示例圖(以x爲節點進行右旋):

                               y
   x                            \                 
  / \      --(右旋)-->           x
 y   z                            \
                                   z

對x進行右旋,意味着,將「x的左孩子」設爲「x的父親節點」;即,將 x變成了一個右節點(x成了爲y的右孩子)! 所以,右旋中的「右」,意味着「被旋轉的節點將變成一個右節點」。 

紅黑樹的基本操做(二) 添加

將一個節點插入到紅黑樹中,須要執行哪些步驟呢?首先,將紅黑樹看成一顆二叉查找樹,將節點插入;而後,將節點着色爲紅色;最後,經過旋轉和從新着色等方法來修正該樹,使之從新成爲一顆紅黑樹。詳細描述以下:

第一步: 將紅黑樹看成一顆二叉查找樹,將節點插入。(樹的鍵值仍然是有序的)

       紅黑樹自己就是一顆二叉查找樹,將節點插入後,該樹仍然是一顆二叉查找樹。也就意味着,樹的鍵值仍然是有序的。此外,不管是左旋仍是右旋,若旋轉以前這棵樹是二叉查找樹,旋轉以後它必定仍是二叉查找樹。這也就意味着,任何的旋轉和從新着色操做,都不會改變它仍然是一顆二叉查找樹的事實。
       好吧?那接下來,咱們就來千方百計的旋轉以及從新着色,使這顆樹從新成爲紅黑樹!

第二步:將插入的節點着色爲"紅色"。
       爲何着色成紅色,而不是黑色呢?爲何呢?在回答以前,咱們須要從新溫習一下紅黑樹的特性:
(1) 每一個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每一個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
(4) 若是一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。
       將插入的節點着色爲紅色,不會違背"特性(5)"!少違背一條特性,就意味着咱們須要處理的狀況越少。接下來,就要努力的讓這棵樹知足其它性質便可;知足了的話,它就又是一顆紅黑樹了。o(∩∩)o...哈哈

第三步: 經過一系列的旋轉或着色等操做,使之從新成爲一顆紅黑樹。
       第二步中,將插入節點着色爲"紅色"以後,不會違背"特性(5)"。那它到底會違背哪些特性呢?
       對於"特性(1)",顯然不會違背了。由於咱們已經將它塗成紅色了。
       對於"特性(2)",顯然也不會違背。在第一步中,咱們是將紅黑樹看成二叉查找樹,而後執行的插入操做。而根據二叉查找數的特色,插入操做不會改變根節點。因此,根節點仍然是黑色。
       對於"特性(3)",顯然不會違背了。這裏的葉子節點是指的空葉子節點,插入非空節點並不會對它們形成影響。
       對於"特性(4)",是有可能違背的!
       那接下來,想辦法使之"知足特性(4)",就能夠將樹從新構形成紅黑樹了。

下面看看代碼究竟是怎樣實現這三步的。 

 根據被插入節點的父節點的狀況,能夠將"當節點z被着色爲紅色節點,並插入二叉樹"劃分爲三種狀況來處理。

① 狀況說明:被插入的節點是根節點。
    處理方法:直接把此節點塗爲黑色。
② 狀況說明:被插入的節點的父節點是黑色。
    處理方法:什麼也不須要作。節點被插入後,仍然是紅黑樹。
③ 狀況說明:被插入的節點的父節點是紅色。
    處理方法:那麼,該狀況與紅黑樹的「特性(5)」相沖突。這種狀況下,被插入節點是必定存在非空祖父節點的;進一步的講,被插入節點也必定存在叔叔節點(即便叔叔節點爲空,咱們也視之爲存在,空節點自己就是黑色節點)。理解這點以後,咱們依據"叔叔節點的狀況",將這種狀況進一步劃分爲3種狀況(Case)。

  現象說明 處理策略
Case 1 當前節點的父節點是紅色,且當前節點的祖父節點的另外一個子節點(叔叔節點)也是紅色。

(01) 將「父節點」設爲黑色。
(02) 將「叔叔節點」設爲黑色。
(03) 將「祖父節點」設爲「紅色」。
(04) 將「祖父節點」設爲「當前節點」(紅色節點);即,以後繼續對「當前節點」進行操做。

Case 2 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的右孩子

(01) 將「父節點」做爲「新的當前節點」。
(02) 以「新的當前節點」爲支點進行左旋。

Case 3 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的左孩子

(01) 將「父節點」設爲「黑色」。
(02) 將「祖父節點」設爲「紅色」。
(03) 以「祖父節點」爲支點進行右旋。

上面三種狀況(Case)處理問題的核心思路都是:將紅色的節點移到根節點;而後,將根節點設爲黑色。下面對它們詳細進行介紹。

 

1. (Case 1)叔叔是紅色

1.1 現象說明
當前節點(即,被插入節點)的父節點是紅色,且當前節點的祖父節點的另外一個子節點(叔叔節點)也是紅色。

1.2 處理策略
(01) 將「父節點」設爲黑色。
(02) 將「叔叔節點」設爲黑色。
(03) 將「祖父節點」設爲「紅色」。
(04) 將「祖父節點」設爲「當前節點」(紅色節點);即,以後繼續對「當前節點」進行操做。

    下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
    「當前節點」和「父節點」都是紅色,違背「特性(4)」。因此,將「父節點」設置「黑色」以解決這個問題。
    可是,將「父節點」由「紅色」變成「黑色」以後,違背了「特性(5)」:由於,包含「父節點」的分支的黑色節點的總數增長了1。  解決這個問題的辦法是:將「祖父節點」由「黑色」變成紅色,同時,將「叔叔節點」由「紅色」變成「黑色」。關於這裏,說明幾點:第一,爲何「祖父節點」以前是黑色?這個應該很容易想明白,由於在變換操做以前,該樹是紅黑樹,「父節點」是紅色,那麼「祖父節點」必定是黑色。 第二,爲何將「祖父節點」由「黑色」變成紅色,同時,將「叔叔節點」由「紅色」變成「黑色」;能解決「包含‘父節點’的分支的黑色節點的總數增長了1」的問題。這個道理也很簡單。「包含‘父節點’的分支的黑色節點的總數增長了1」 同時也意味着 「包含‘祖父節點’的分支的黑色節點的總數增長了1」,既然這樣,咱們經過將「祖父節點」由「黑色」變成「紅色」以解決「包含‘祖父節點’的分支的黑色節點的總數增長了1」的問題; 可是,這樣處理以後又會引發另外一個問題「包含‘叔叔’節點的分支的黑色節點的總數減小了1」,如今咱們已知「叔叔節點」是「紅色」,將「叔叔節點」設爲「黑色」就能解決這個問題。 因此,將「祖父節點」由「黑色」變成紅色,同時,將「叔叔節點」由「紅色」變成「黑色」;就解決了該問題。
    按照上面的步驟處理以後:當前節點、父節點、叔叔節點之間都不會違背紅黑樹特性,但祖父節點卻不必定。若此時,祖父節點是根節點,直接將祖父節點設爲「黑色」,那就徹底解決這個問題了;若祖父節點不是根節點,那咱們須要將「祖父節點」設爲「新的當前節點」,接着對「新的當前節點」進行分析。

1.3 示意圖

 

2. (Case 2)叔叔是黑色,且當前節點是右孩子

2.1 現象說明
當前節點(即,被插入節點)的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的右孩子

2.2 處理策略
(01) 將「父節點」做爲「新的當前節點」。
(02) 以「新的當前節點」爲支點進行左旋。

      下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
      首先,將「父節點」做爲「新的當前節點」;接着,以「新的當前節點」爲支點進行左旋。 爲了便於理解,咱們先說明第(02)步,再說明第(01)步;爲了便於說明,咱們設置「父節點」的代號爲F(Father),「當前節點」的代號爲S(Son)。
爲何要「以F爲支點進行左旋」呢?根據已知條件可知:S是F的右孩子。而以前咱們說過,咱們處理紅黑樹的核心思想:將紅色的節點移到根節點;而後,將根節點設爲黑色。既然是「將紅色的節點移到根節點」,那就是說要不斷的將破壞紅黑樹特性的紅色節點上移(即向根方向移動)。 而S又是一個右孩子,所以,咱們能夠經過「左旋」來將S上移! 
      按照上面的步驟(以F爲支點進行左旋)處理以後:若S變成了根節點,那麼直接將其設爲「黑色」,就徹底解決問題了;若S不是根節點,那咱們須要執行步驟(01),即「將F設爲‘新的當前節點’」。那爲何不繼續以S爲新的當前節點繼續處理,而須要以F爲新的當前節點來進行處理呢?這是由於「左旋」以後,F變成了S的「子節點」,即S變成了F的父節點;而咱們處理問題的時候,須要從下至上(由葉到根)方向進行處理;也就是說,必須先解決「孩子」的問題,再解決「父親」的問題;因此,咱們執行步驟(01):將「父節點」做爲「新的當前節點」。

2.2 示意圖

 

3. (Case 3)叔叔是黑色,且當前節點是左孩子

3.1 現象說明
當前節點(即,被插入節點)的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的左孩子

3.2 處理策略
(01) 將「父節點」設爲「黑色」。
(02) 將「祖父節點」設爲「紅色」。
(03) 以「祖父節點」爲支點進行右旋。

      下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
      爲了便於說明,咱們設置「當前節點」爲S(Original Son),「兄弟節點」爲B(Brother),「叔叔節點」爲U(Uncle),「父節點」爲F(Father),祖父節點爲G(Grand-Father)。
      S和F都是紅色,違背了紅黑樹的「特性(4)」,咱們能夠將F由「紅色」變爲「黑色」,就解決了「違背‘特性(4)’」的問題;但卻引發了其它問題:違背特性(5),由於將F由紅色改成黑色以後,全部通過F的分支的黑色節點的個數增長了1。那咱們如何解決「全部通過F的分支的黑色節點的個數增長了1」的問題呢? 咱們能夠經過「將G由黑色變成紅色」,同時「以G爲支點進行右旋」來解決。

2.3 示意圖

提示:上面的進行Case 3處理以後,再將節點"120"看成當前節點,就變成了Case 2的狀況。

 

紅黑樹的基本操做(三) 刪除

將紅黑樹內的某一個節點刪除。須要執行的操做依次是:首先,將紅黑樹看成一顆二叉查找樹,將該節點從二叉查找樹中刪除;而後,經過"旋轉和從新着色"等一系列來修正該樹,使之從新成爲一棵紅黑樹。詳細描述以下:

第一步:將紅黑樹看成一顆二叉查找樹,將節點刪除。
       這和"刪除常規二叉查找樹中刪除節點的方法是同樣的"。分3種狀況:
       ① 被刪除節點沒有兒子,即爲葉節點。那麼,直接將該節點刪除就OK了。
       ② 被刪除節點只有一個兒子。那麼,直接刪除該節點,並用該節點的惟一子節點頂替它的位置。
       ③ 被刪除節點有兩個兒子。那麼,先找出它的後繼節點;而後把「它的後繼節點的內容」複製給「該節點的內容」;以後,刪除「它的後繼節點」。在這裏,後繼節點至關於替身,在將後繼節點的內容複製給"被刪除節點"以後,再將後繼節點刪除。這樣就巧妙的將問題轉換爲"刪除後繼節點"的狀況了,下面就考慮後繼節點。 在"被刪除節點"有兩個非空子節點的狀況下,它的後繼節點不多是雙子非空。既然"的後繼節點"不可能雙子都非空,就意味着"該節點的後繼節點"要麼沒有兒子,要麼只有一個兒子。若沒有兒子,則按"狀況① "進行處理;若只有一個兒子,則按"狀況② "進行處理。

第二步:經過"旋轉和從新着色"等一系列來修正該樹,使之從新成爲一棵紅黑樹。
       由於"第一步"中刪除節點以後,可能會違背紅黑樹的特性。因此須要經過"旋轉和從新着色"來修正該樹,使之從新成爲一棵紅黑樹。

  下面對刪除函數進行分析。在分析以前,咱們再次溫習一下紅黑樹的幾個特性:

(1) 每一個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每一個葉子節點是黑色。 [注意:這裏葉子節點,是指爲空的葉子節點!]
(4) 若是一個節點是紅色的,則它的子節點必須是黑色的。
(5) 從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點。

      前面咱們將"刪除紅黑樹中的節點"大體分爲兩步,在第一步中"將紅黑樹看成一顆二叉查找樹,將節點刪除"後,可能違反"特性(2)、(4)、(5)"三個特性。第二步須要解決上面的三個問題,進而保持紅黑樹的所有特性。
      爲了便於分析,咱們假設"x包含一個額外的黑色"(x本來的顏色還存在),這樣就不會違反"特性(5)"。爲何呢?
      經過RB-DELETE算法,咱們知道:刪除節點y以後,x佔據了原來節點y的位置。 既然刪除y(y是黑色),意味着減小一個黑色節點;那麼,再在該位置上增長一個黑色便可。這樣,當咱們假設"x包含一個額外的黑色",就正好彌補了"刪除y所丟失的黑色節點",也就不會違反"特性(5)"。 所以,假設"x包含一個額外的黑色"(x本來的顏色還存在),這樣就不會違反"特性(5)"。
      如今,x不只包含它本來的顏色屬性,x還包含一個額外的黑色。即x的顏色屬性是"紅+黑"或"黑+黑",它違反了"特性(1)"。

      如今,咱們面臨的問題,由解決"違反了特性(2)、(4)、(5)三個特性"轉換成了"解決違反特性(1)、(2)、(4)三個特性"。RB-DELETE-FIXUP須要作的就是經過算法恢復紅黑樹的特性(1)、(2)、(4)。RB-DELETE-FIXUP的思想是:將x所包含的額外的黑色不斷沿樹上移(向根方向移動),直到出現下面的姿態:
a) x指向一個"紅+黑"節點。此時,將x設爲一個"黑"節點便可。
b) x指向根。此時,將x設爲一個"黑"節點便可。
c) 非前面兩種姿態。

將上面的姿態,能夠歸納爲3種狀況。
① 狀況說明:x是「紅+黑」節點。
    處理方法:直接把x設爲黑色,結束。此時紅黑樹性質所有恢復。
② 狀況說明:x是「黑+黑」節點,且x是根。
    處理方法:什麼都不作,結束。此時紅黑樹性質所有恢復。
③ 狀況說明:x是「黑+黑」節點,且x不是根。
    處理方法:這種狀況又能夠劃分爲4種子狀況。這4種子狀況以下表所示:

  現象說明 處理策略
Case 1 x是"黑+黑"節點,x的兄弟節點是紅色。(此時x的父節點和x的兄弟節點的子節點都是黑節點)。

(01) 將x的兄弟節點設爲「黑色」。
(02) 將x的父節點設爲「紅色」。
(03) 對x的父節點進行左旋。
(04) 左旋後,從新設置x的兄弟節點。

Case 2 x是「黑+黑」節點,x的兄弟節點是黑色,x的兄弟節點的兩個孩子都是黑色。

(01) 將x的兄弟節點設爲「紅色」。
(02) 設置「x的父節點」爲「新的x節點」。

Case 3 x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的左孩子是紅色,右孩子是黑色的。

(01) 將x兄弟節點的左孩子設爲「黑色」。
(02) 將x兄弟節點設爲「紅色」。
(03) 對x的兄弟節點進行右旋。
(04) 右旋後,從新設置x的兄弟節點。

Case 4 x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的右孩子是紅色的,x的兄弟節點的左孩子任意顏色。

(01) 將x父節點顏色 賦值給 x的兄弟節點。
(02) 將x父節點設爲「黑色」。
(03) 將x兄弟節點的右子節設爲「黑色」。
(04) 對x的父節點進行左旋。
(05) 設置「x」爲「根節點」。

 

1. (Case 1)x是"黑+黑"節點,x的兄弟節點是紅色

1.1 現象說明
x是"黑+黑"節點,x的兄弟節點是紅色。(此時x的父節點和x的兄弟節點的子節點都是黑節點)。

1.2 處理策略
(01) 將x的兄弟節點設爲「黑色」。
(02) 將x的父節點設爲「紅色」。
(03) 對x的父節點進行左旋。
(04) 左旋後,從新設置x的兄弟節點。

      下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
      這樣作的目的是將「Case 1」轉換爲「Case 2」、「Case 3」或「Case 4」,從而進行進一步的處理。對x的父節點進行左旋;左旋後,爲了保持紅黑樹特性,就須要在左旋前「將x的兄弟節點設爲黑色」,同時「將x的父節點設爲紅色」;左旋後,因爲x的兄弟節點發生了變化,須要更新x的兄弟節點,從而進行後續處理。

1.3 示意圖

 

2. (Case 2) x是"黑+黑"節點,x的兄弟節點是黑色,x的兄弟節點的兩個孩子都是黑色

2.1 現象說明
x是「黑+黑」節點,x的兄弟節點是黑色,x的兄弟節點的兩個孩子都是黑色。

2.2 處理策略
(01) 將x的兄弟節點設爲「紅色」。
(02) 設置「x的父節點」爲「新的x節點」。

      下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
      這個狀況的處理思想:是將「x中多餘的一個黑色屬性上移(往根方向移動)」。 x是「黑+黑」節點,咱們將x由「黑+黑」節點 變成 「黑」節點,多餘的一個「黑」屬性移到x的父節點中,即x的父節點多出了一個黑屬性(若x的父節點原先是「黑」,則此時變成了「黑+黑」;若x的父節點原先時「紅」,則此時變成了「紅+黑」)。 此時,須要注意的是:全部通過x的分支中黑節點個數沒變化;可是,全部通過x的兄弟節點的分支中黑色節點的個數增長了1(由於x的父節點多了一個黑色屬性)!爲了解決這個問題,咱們須要將「全部通過x的兄弟節點的分支中黑色節點的個數減1」便可,那麼就能夠經過「將x的兄弟節點由黑色變成紅色」來實現。
      通過上面的步驟(將x的兄弟節點設爲紅色),多餘的一個顏色屬性(黑色)已經跑到x的父節點中。咱們須要將x的父節點設爲「新的x節點」進行處理。若「新的x節點」是「黑+紅」,直接將「新的x節點」設爲黑色,便可徹底解決該問題;若「新的x節點」是「黑+黑」,則須要對「新的x節點」進行進一步處理。

2.3 示意圖

 

3. (Case 3)x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的左孩子是紅色,右孩子是黑色的

3.1 現象說明
x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的左孩子是紅色,右孩子是黑色的。

3.2 處理策略
(01) 將x兄弟節點的左孩子設爲「黑色」。
(02) 將x兄弟節點設爲「紅色」。
(03) 對x的兄弟節點進行右旋。
(04) 右旋後,從新設置x的兄弟節點。

       下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
       咱們處理「Case 3」的目的是爲了將「Case 3」進行轉換,轉換成「Case 4」,從而進行進一步的處理。轉換的方式是對x的兄弟節點進行右旋;爲了保證右旋後,它仍然是紅黑樹,就須要在右旋前「將x的兄弟節點的左孩子設爲黑色」,同時「將x的兄弟節點設爲紅色」;右旋後,因爲x的兄弟節點發生了變化,須要更新x的兄弟節點,從而進行後續處理。

3.3 示意圖

 

4. (Case 4)x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的右孩子是紅色的,x的兄弟節點的左孩子任意顏色

4.1 現象說明
x是「黑+黑」節點,x的兄弟節點是黑色;x的兄弟節點的右孩子是紅色的,x的兄弟節點的左孩子任意顏色。

4.2 處理策略
(01) 將x父節點顏色 賦值給 x的兄弟節點。
(02) 將x父節點設爲「黑色」。
(03) 將x兄弟節點的右子節設爲「黑色」。
(04) 對x的父節點進行左旋。
(05) 設置「x」爲「根節點」。

      下面談談爲何要這樣處理。(建議理解的時候,經過下面的圖進行對比)
      咱們處理「Case 4」的目的是:去掉x中額外的黑色,將x變成單獨的黑色。處理的方式是「:進行顏色修改,而後對x的父節點進行左旋。下面,咱們來分析是如何實現的。
      爲了便於說明,咱們設置「當前節點」爲S(Original Son),「兄弟節點」爲B(Brother),「兄弟節點的左孩子」爲BLS(Brother's Left Son),「兄弟節點的右孩子」爲BRS(Brother's Right Son),「父節點」爲F(Father)。
      咱們要對F進行左旋。但在左旋前,咱們須要調換F和B的顏色,並設置BRS爲黑色。爲何須要這裏處理呢?由於左旋後,F和BLS是父子關係,而咱們已知BL是紅色,若是F是紅色,則違背了「特性(4)」;爲了解決這一問題,咱們將「F設置爲黑色」。 可是,F設置爲黑色以後,爲了保證知足「特性(5)」,即爲了保證左旋以後:
      第一,「同時通過根節點和S的分支的黑色節點個數不變」。
             若知足「第一」,只須要S丟棄它多餘的顏色便可。由於S的顏色是「黑+黑」,而左旋後「同時通過根節點和S的分支的黑色節點個數」增長了1;如今,只需將S由「黑+黑」變成單獨的「黑」節點,便可知足「第一」。
      第二,「同時通過根節點和BLS的分支的黑色節點數不變」。
             若知足「第二」,只須要將「F的原始顏色」賦值給B便可。以前,咱們已經將「F設置爲黑色」(即,將B的顏色"黑色",賦值給了F)。至此,咱們算是調換了F和B的顏色。
      第三,「同時通過根節點和BRS的分支的黑色節點數不變」。
             在「第二」已經知足的狀況下,若要知足「第三」,只須要將BRS設置爲「黑色」便可。
通過,上面的處理以後。紅黑樹的特性所有獲得的知足!接着,咱們將x設爲根節點,就能夠跳出while循環(參考僞代碼);即完成了所有處理。

至此,咱們就完成了Case 4的處理。理解Case 4的核心,是瞭解如何「去掉當前節點額外的黑色」。

4.3 示意圖

   

 

紅黑樹-插入實例

前言
本篇博客咱們說一說如何從無到有建立一顆紅黑樹,並在建立紅黑樹的過程當中理解其構造原理。

紅黑樹規則
首先咱們複習一下紅黑樹的五項規則,以下:
- 規則一:每一個節點或者是黑色,或者是紅色;
- 規則二:根節點是黑色;
- 規則三:每一個葉子節點(NIL/NULL)是黑色;
- 規則四:若是一個節點是紅色的,則它的子節點必須是黑色的;
- 規則五:從一個節點到該節點的子孫節點的全部路徑上包含相同數目的黑節點;

插入規則
其次咱們知道要將一個節點N插入到紅黑樹的時候是要根據二叉查找樹的規則進行的,即比某節點X小往其左子樹走,比某節點X大則往其右子樹走,一直比較下去直至葉子節點;

待插入節點的顏色
而後咱們須要對待插入的節點進行着色,那麼着什麼色呢?答案是紅色,但是爲何呢?黑色不行嗎?

結合上面說過的紅黑樹的五項規則咱們知道,若是把待插入的節點着色爲黑色,那麼插入新節點後的紅黑樹必定是違背規則五的,因此每次插入新節點後都須要進行修正;

而若是把待插入的節點着色爲紅色,插入後的紅黑樹確定是不會違背規則五,可是可能會違背規則二或者規則四(每次插入最多隻可能會違背其中一項),因此每次插入後紅黑樹可能會須要修正,也有可能不須要修正,這就意味着咱們須要處理的狀況會比着色爲黑色的狀況要少!

修正規則
什麼狀況下須要對插入新節點後的紅黑樹進行修正?

咱們須要結合紅黑樹的五項規則和待插入節點的顏色爲紅色這兩點來看!

當新插入一個節點時,首先規則一和規則三不用管,又由於待插入節點的顏色爲紅色的,因此規則五也不用管(可是在修正過程當中可能會違背規則五);

而對於規則二,只有在插入第一個節點或者修正過程當中會違背(插入第一個節點時確定違背,由於待插入節點的顏色爲紅色),可是處理策略也很簡單,直接將新插入的節點/當前節點從新着色爲黑色便可;

若是新插入節點的父節點是黑色,那麼不用管,插入後的紅黑樹仍是符合規則的,若是新插入節點的父節點是紅色,那麼就須要進行修正了,由於違背了規則四,具體處理策略以下:

違背規則四時 現象說明 處理策略
狀況一 當前節點的父節點是紅色,且當前節點的祖父節點的另外一個子節點(叔叔節點)也是紅色 (01) 將父節點設爲黑色;
(02) 將叔叔節點設爲黑色;
(03) 將祖父節點設爲紅色;
(04) 將祖父節點設爲當前節點(紅色節點);
(05)以後繼續對當前節點進行判斷是否違背規則並操做;
狀況二 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的右孩子 (01) 將父節點設爲黑色;
(02) 將祖父節點設爲紅色;
(03) 以祖父節點爲支點進行左旋;
狀況三 當前節點的父節點是紅色,叔叔節點是黑色,且當前節點是其父節點的左孩子 (01) 將父節點做爲新的當前節點;
(02) 以新的當前節點爲支點進行右旋;
(03)以後繼續對新的當前節點進行判斷是否違背規則並操做;
插入的狀況參考:紅黑樹原理以及插入、刪除算法 附圖例說明

參考:在線生成紅黑樹(含變形步驟) 

參考:紅黑樹-插入

參考:紅黑樹(一)之 原理和算法詳細介紹

相關文章
相關標籤/搜索