1. 程式人生 > >【數據結構】紅黑樹

【數據結構】紅黑樹

png 兩種 叔叔 路徑 之前 ase 技術分享 操作性 splay

一、紅黑樹的定義:


  (1)根節點是黑色的,

  (2)所有葉子節點上不存儲數據,並且顏色都為黑色。

  (3)紅色節點相鄰的節點不能為紅色。(紅色節點鄰居節點只能是黑色節點)

  (4)每一個節點,從該節點到達可達葉子節點的所有路徑都包含了相同數量的黑色節點。

圖示例子

  技術分享圖片

  紅黑樹的時間復雜度為O(log n)。由平衡二叉樹我們可以知道其高度約為 log2n(再添加數據時通過不斷的調整達到平衡)。而紅黑樹的高度為多少呢?

  這裏有一個定理:一個含有n個節點的紅黑樹的高度最多為2log n。我們可以這樣來理解: 對於上面的那個圖示,如果我們去掉所有的紅色節點,結果會變成下圖。

技術分享圖片

  這時由於有些節點沒有父節點了,我們直接拿到他的祖父節點來充當其父節點,所以之前的二叉樹就變成了四叉樹。在前面紅黑樹的定義裏有這麽一條:從任意節點到可達的葉子節點的每個路徑包含相同數目的黑色節點。我們從四叉樹中取出某些節點,放到葉節點位置,四叉樹就變成了完全二叉樹。所以,僅包含黑色節點的四叉樹的高度,比包含相同節點個數的完全二叉樹的高度log2n還要小。而現在當我們再把紅色節點加回去時,由於前面的定義紅色點之間不能相鄰,所以現在紅黑樹的高度會比log2n大,但是最大不會超過2log2n(因為之前全部黑色節點時,的高度不會超過log2n)。

總結

  因此,由於紅黑樹的高度只比平衡樹的高度大了一倍,但是在性能上,下降的並不是很多。在實際運用中,AVL 樹是一種高度平衡的二叉樹,查找的效率非常高,但是,AVL 樹為了維持這種高度的平衡,就需要進行更多的操作來保持這種平衡。每次插入、刪除都要做調整,就比較復雜、耗時。所以,對於有頻繁的插入、刪除操作的數據集合,使用 AVL 樹的代價就有點高了。而紅黑樹只是做到了近似平衡,並不是嚴格的平衡,所以在維護平衡的成本上,要比 AVL 樹要低。所以,紅黑樹的插入、刪除、查找各種操作性能都比較穩定。對於工程應用來說,要面對各種異常情況,為了支撐這種工業級的應用,我們更傾向於這種性能穩定的平衡二叉查找樹。

二、紅黑樹的操作


  在插入數據和刪除數據時,都可能會破壞紅黑樹的定義。因此我們需要通過一定的方法進行調整來使得紅黑樹達到滿足定義和平衡。

  在紅黑樹調整策略中有兩個基本的操作就是左旋和右旋。

  左旋

技術分享圖片

  左旋的處理規則(有助於理解旋轉規則) 

 1 LEFT-ROTATE(T, x)  
 2  y ← right[x]            // 前提:這裏假設x的右孩子為y。下面開始正式操作
 3  right[x] ← left[y]      // 將 “y的左孩子” 設為 “x的右孩子”,即 將β設為x的右孩子
4 p[left[y]] ← x // 將 “x” 設為 “y的左孩子的父親”,即 將β的父親設為x 5 p[y] ← p[x] // 將 “x的父親” 設為 “y的父親” 6 if p[x] = nil[T] 7 then root[T] ← y // 情況1:如果 “x的父親” 是空節點,則將y設為根節點 8 else if x = left[p[x]] 9 then left[p[x]] ← y // 情況2:如果 x是它父節點的左孩子,則將y設為“x的父節點的左孩子” 10 else right[p[x]] ← y // 情況3:(x是它父節點的右孩子) 將y設為“x的父節點的右孩子” 11 left[y] ← x // 將 “x” 設為 “y的左孩子” 12 p[x] ← y // 將 “x的父節點” 設為 “y”

  右旋

技術分享圖片

 旋的處理規則:

 1 RIGHT-ROTATE(T, y)  
 2  x ← left[y]             // 前提:這裏假設y的左孩子為x。下面開始正式操作
 3  left[y] ← right[x]      // 將 “x的右孩子” 設為 “y的左孩子”,即 將β設為y的左孩子
 4  p[right[x]] ← y         // 將 “y” 設為 “x的右孩子的父親”,即 將β的父親設為y
 5  p[x] ← p[y]             // 將 “y的父親” 設為 “x的父親”
 6  if p[y] = nil[T]       
 7  then root[T] ← x                 // 情況1:如果 “y的父親” 是空節點,則將x設為根節點
 8  else if y = right[p[y]]  
 9            then right[p[y]] ← x   // 情況2:如果 y是它父節點的右孩子,則將x設為“y的父節點的左孩子”
10            else left[p[y]] ← x    // 情況3:(y是它父節點的左孩子) 將x設為“y的父節點的左孩子”
11  right[x] ← y            // 將 “y” 設為 “x的右孩子”
12  p[y] ← x                // 將 “y的父節點” 設為 “x”

插入操作

  紅黑樹規定插入的節點必須是紅色,並且插入的節點都是放在葉子節點上。因此,對於插入操作有兩種特殊的情況比較好處理:

  (1) 插入節點的父節點是黑色的,這種情況滿足紅黑樹的定義,因此不需要做什麽操作。

  (2)如果插入的節點是根節點,我們只需要將其變成黑色的就行了。

  其他的情況他都會破壞紅黑樹的定義,因此需要借助左右旋或改變顏色來進行調整。

技術分享圖片

case 1:當前節點的父節點是紅色,且當前節點的祖父節點的另一個子節點(叔叔節點)也是紅色。

技術分享圖片

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

技術分享圖片

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

技術分享圖片

刪除操作


待續

【數據結構】紅黑樹