圖說紅黑樹算法

1. 二叉樹

對於一顆普通的二叉樹來說,查找時候還是蠻有效率的,只要不是極端的情況。例如下面這種滿二叉樹來說,時間複雜度爲O(logn)。
二叉樹
但是呢,有些時候並不是那麼如你所願,例如,從10到1一直插入到一顆二叉樹時,就會出現以下這種情況,查找的時候複雜度實在難受,O(n)
極端二叉樹
然而對於這種情況的二叉樹,每次插入的時候都只管找位置就坐,可能導致樹的左右不平衡發展了,這,就是懶的結果了。那麼,爲了讓一顆二叉樹保持很好的查詢效率,於是乎就產生了:會自我管理樹的平衡性的二叉樹,稱爲:紅黑樹(Red-Black tree)

2. 什麼是紅黑樹

先來看看一顆標準的紅黑樹是怎麼樣的:
紅黑樹例子

  1. 節點只有兩種顏色:紅和黑;
  2. 根節點和空葉子節點(nil)都爲黑
  3. 如果某個節點爲紅色,它的孩子結果都是黑色
  4. 任意一個節點到它的空葉子節點(nil)路徑上包含相同數量的黑色節點,稱之爲黑色高度

### 需要額外注意的是:最長的路徑(路上節點從黑開始黑紅相隔)不能大於2倍的最短路徑(路上節點全是黑)

3. 紅黑樹的操作

  1. 查找
  2. 插入
  3. 刪除

對於插入和刪除兩個操作,有可能會破壞紅黑樹的平衡,使其不滿足紅黑樹的所有屬性。對於破壞平衡的情況,需要對節點進行調整來保持平衡,自我管理能力槓槓的。
那怎麼調整呢?大體思路是:調整子樹來改變樹的結構和修改節點顏色兩個主要操作來保持平衡。同時需要朝兩個目標處理,

  1. 降低樹的高度(以防出現一邊倒的情況)
    • 保持最大高度爲O(logn)
    • 大顆的子樹向上移,小顆的子樹向下挪
  2. 不影響樹的有序性

修改節點顏色,主要是:紅變黑,或黑變紅。現在主要來看一下,如何調整子樹結構。

3.1逆時針旋轉(左旋轉)和順時針旋轉(右旋轉)

個人覺得,順逆時針會比較好理解,文章以此來描述。旋轉操作是指通過旋轉某個節點,同時來調整子樹的結構,牽一髮而動子樹的效果。舉個逆時針的例子,如下圖

把節點A按逆時針旋轉,步驟是:

  1. 把節點A的右孩子,變成它的父節點(P’)
  2. 把P’的左孩子變成節點A的右孩子

解析:由於第1步操作完之後,P’可能存在3個子節點,不符合二叉樹的性質,所以需要把其中一個過繼到其它節點上;完成第二步之後,就可以把P’的子節點變成2個了
根據步驟把上面的樹旋轉一下(顏色可以先忽略,主要是講旋轉邏輯):
逆時針旋轉
好了,逆時針的例子解析完了,對於順時針應該是可以舉一反三了,因爲它們是對稱的,操作的時候把節點的左換成右,右換成左即可。下面把原始圖的結果列一下。
順時針旋轉

也許聰明的小夥伴可以看到,對於同一個子樹,逆時順和順時針操作完之後,其實就變回去原來的結構了,可以仔細看看上面兩個例子

3.2 插入操作

維護一顆紅黑樹性質不變,當插入或移除節點的時候,如果這個操作破壞了紅黑樹的屬性,則需要把它調整一下。主要步驟有:

  1. 插入節點Node, 並且標記爲紅色(改不了字體顏色,算了)
  2. 把相關節點調整顏色和旋轉節點來保持紅黑樹的屬性。

敲黑板了,爲什麼要標紅色?因爲:標紅只會破壞紅黑樹的兩條屬性。(往上翻,第2條和第3條)

當插入一個新的節點(Node)時,此時可能有以下4種情況:

  1. Node爲根節點
  2. Node的叔叔爲紅色
  3. Node的叔叔爲黑色(這種情況還細分爲兩種)
    1. Node的爺爺, 父親,和Node自己本身都是一個分支方向繼承下來的,稱之爲直線型關係。如下圖:在這裏插入圖片描述
    2. Node的爺爺和父親關係方向,與Node和Node父親的關係方向不一樣,稱之爲三角型關係。如下圖:在這裏插入圖片描述

現在來詳說一下對於以上情況每一種情況的調整步驟,統一定義新插入的節點爲Node
Case 1: Node爲根節點

  • 把Node從紅色標爲黑色即可,這是最簡單的情況

Case 2: Node的叔叔爲紅色

  • 把祖上兩輩的節點顏色取反上色,即把爺爺,父親和叔叔節點取反上色。過程圖如下:在這裏插入圖片描述
    Case 3: Node的叔叔爲黑色(直接型關係)
  • 旋轉爺爺節點到另一側(根據當前狀態選擇順逆時針)
  • 把Node的原始爺爺節點和父親節點重新上色在這裏插入圖片描述
    Case 4:Node的叔叔爲黑色(三色型關係)
  • 順時針旋轉Node的父節點在這裏插入圖片描述
    以上就是紅黑樹的基礎內容與插入操作相關情況和步驟。下面我們來用例子演示一下。

4.例子

我們先從簡單的開始吧。模擬插入數據【10, 5, 1】

  1. 在一顆空樹上插入一個節點10(符合插入操作的Case 1的情況)插入10時
  2. 再插入一個節點5,符合正常紅黑樹,不需要處理插入5時
  3. 再插入一個節點1,破壞規則3(紅色節點的子節點都是黑色),故需要調整。根據目前狀態選擇調整策略:節點1的叔叔節點爲黑色的空節點,並且是直線型關係,所以採用Case 3處理過程。根據當前狀態,順時針旋轉爺爺節點,然後再把原始的爺爺節點和父親節點的顏色修改一下,於是可以恢復到正常的紅黑樹了。插入1時

這個就是簡單的自我調整平衡的二叉樹,亦即是紅黑樹。它的應用場景比較明確,就是有序的map會使用到它,例如C++的map,它是有序的,並且查找時間複雜度爲O(logn)。篇幅有限,刪除操作留待後面再補,因爲實在太長了。感謝你的耐心閱讀,希望能對你有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章