紅黑樹詳解

轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7740956
一、紅黑樹概述

     紅黑樹和我們以前學過的AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能。不過自從紅黑樹出來後,AVL樹就被放到了博物館裏,據說是紅黑樹有更好的效率,更高的統計性能。這一點在我們瞭解了紅黑樹的實現原理後,就會有更加深切的體會。
     紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。學過數據結構的人應該都已經領教過AVL樹的複雜,但AVL樹的複雜比起紅黑樹來說簡直是小巫見大巫,紅黑樹纔是真正的變態級數據結構。
     由於STL中的關聯式容器默認的底層實現都是紅黑樹,因此紅黑樹對於後續學習STL源碼還是很重要的,有必要掌握紅黑樹的實現原理和源碼實現。
     紅黑樹是AVL樹的變種,紅黑樹通過一些着色法則確保沒有一條路徑會比其它路徑長出兩倍,因而達到接近平衡的目的。所謂紅黑樹,不僅是一個二叉搜索樹,而且必須滿足一下規則:
     1、每個節點不是紅色就是黑色。
     2、根節點爲黑色。
     3、如果節點
爲紅色,其子節點必須爲黑色
     4、任意一個節點到到NULL(樹尾端)的任何路徑,所含之黑色節點數必須相同。

上面的這些約束保證了這個樹大致上是平衡的,這也決定了紅黑樹的插入、刪除、查詢等操作是比較快速的。 根據規則4,新增節點必須爲紅色;根據規則3,新增節點之父節點必須爲黑色。當新增節點根據二叉搜索樹的規則到達其插入點時,卻未能符合上述條件時,就必須調整顏色並旋轉樹形,如下圖:

假設我們爲上圖分別插入節點3、8、35、75,根據二叉搜索樹的規則,插入這四個節點後,我們會發現它們都破壞了紅黑樹的規則,因此我們必須調整樹形,也就是旋轉樹形並改變節點的顏色。

二、紅黑樹上結點的插入

      在討論紅黑樹的插入操作之前必須要明白,任何一個即將插入的新結點的初始顏色都爲紅色。這一點很容易理解,因爲插入黑點會增加某條路徑上黑結點的數目,從而導致整棵樹黑高度的不平衡。但如果新結點的父結點爲紅色時(如下圖所示),將會違反紅黑樹的性質:一條路徑上不能出現相鄰的兩個紅色結點。這時就需要通過一系列操作來使紅黑樹保持平衡。

      爲了清楚地表示插入操作以下在結點中使用“新”字表示一個新插入的結點;使用“父”字表示新插入點的父結點;使用“叔”字表示“父”結點的兄弟結點;使用“祖”字表示“父”結點的父結點。插入操作分爲以下幾種情況:
1、黑父
     如下圖所示,如果新節點的父結點爲黑色結點,那麼插入一個紅點將不會影響紅黑樹的平衡,此時插入操作完成。紅黑樹比AVL樹優秀的地方之一在於黑父的情況比較常見,從而使紅黑樹需要旋轉的機率相對AVL樹來說會少一些。

2、紅父
     如果新節點的父結點爲紅色,這時就需要進行一系列操作以保證整棵樹紅黑性質。如下圖所示,由於父結點爲紅色,此時可以判定,祖父結點必定爲黑色。這時需要根據叔父結點的顏色來決定做什麼樣的操作。青色結點表示顏色未知。由於有可能需要根結點到新點的路徑上進行多次旋轉操作,而每次進行不平衡判斷的起始點(我們可將其視爲新點)都不一樣。所以我們在此使用一個藍色箭頭指向這個起始點,並稱之爲判定點。

2.1 紅叔
當叔父結點爲紅色時,如下圖所示,無需進行旋轉操作,只要將父和叔結點變爲黑色,將祖父結點變爲紅色即可。但由於祖父結點的父結點有可能爲紅色,從而違反紅黑樹性質。此時必須將祖父結點作爲新的判定點繼續向上(迭代)進行平衡操作。

需要注意的是,無論“父節點”在“叔節點”的左邊還是右邊,無論“新節點”是“父節點”的左孩子還是右孩子,它們的操作都是完全一樣的(其實這種情況包括4種,只需調整顏色,不需要旋轉樹形)。
2.2 黑叔
當叔父結點爲黑色時,需要進行旋轉,以下圖示了所有的旋轉可能:
Case 1:

Case 2:

Case 3:

Case 4:


      可以觀察到,當旋轉完成後,新的旋轉根全部爲黑色,此時不需要再向上回溯進行平衡操作,插入操作完成。需要注意,上面四張圖的“叔”、“1”、“2”、“3”結點有可能爲黑哨兵結點。
      其實紅黑樹的插入操作不是很難,甚至比AVL樹的插入操作還更簡單些。紅黑樹的插入操作源代碼如下:
[cpp] view plain copy
  1. // 元素插入操作  insert_unique()  
  2. // 插入新值:節點鍵值不允許重複,若重複則插入無效  
  3. // 注意,返回值是個pair,第一個元素是個紅黑樹迭代器,指向新增節點  
  4. // 第二個元素表示插入成功與否  
  5. template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>  
  6. pair<typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator , bool>  
  7. rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::insert_unique(const Value &v)  
  8. {  
  9.     rb_tree_node* y = header;    // 根節點root的父節點  
  10.     rb_tree_node* x = root();    // 從根節點開始  
  11.     bool comp = true;  
  12.     while(x != 0)  
  13.     {  
  14.         y = x;  
  15.         comp = key_compare(KeyOfValue()(v) , key(x));    // v鍵值小於目前節點之鍵值?  
  16.         x = comp ? left(x) : right(x);   // 遇“大”則往左,遇“小於或等於”則往右  
  17.     }  
  18.     // 離開while循環之後,y所指即插入點之父節點(此時的它必爲葉節點)  
  19.     iterator j = iterator(y);     // 令迭代器j指向插入點之父節點y  
  20.     if(comp)     // 如果離開while循環時comp爲真(表示遇“大”,將插入於左側)  
  21.     {  
  22.         if(j == begin())    // 如果插入點之父節點爲最左節點  
  23.             return pair<iterator , bool>(_insert(x , y , z) , true);  
  24.         else     // 否則(插入點之父節點不爲最左節點)  
  25.             --j;   // 調整j,回頭準備測試  
  26.     }  
  27.     if(key_compare(key(j.node) , KeyOfValue()(v) ))  
  28.         // 新鍵值不與既有節點之鍵值重複,於是以下執行安插操作  
  29.         return pair<iterator , bool>(_insert(x , y , z) , true);  
  30.     // 以上,x爲新值插入點,y爲插入點之父節點,v爲新值  
  31.   
  32.     // 進行至此,表示新值一定與樹中鍵值重複,那麼就不應該插入新值  
  33.     return pair<iterator , bool>(j , false);  
  34. }  
  35.   
  36. // 真正地插入執行程序 _insert()  
  37. template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>  
  38. typename<Key , Value , KeyOfValue , Compare , Alloc>::_insert(base_ptr x_ , base_ptr y_ , const Value &v)  
  39. {  
  40.     // 參數x_ 爲新值插入點,參數y_爲插入點之父節點,參數v爲新值  
  41.     link_type x = (link_type) x_;  
  42.     link_type y = (link_type) y_;  
  43.     link_type z;  
  44.   
  45.     // key_compare 是鍵值大小比較準則。應該會是個function object  
  46.     if(y == header || x != 0 || key_compare(KeyOfValue()(v) , key(y) ))  
  47.     {  
  48.         z = create_node(v);    // 產生一個新節點  
  49.         left(y) = z;           // 這使得當y即爲header時,leftmost() = z  
  50.         if(y == header)  
  51.         {  
  52.             root() = z;  
  53.             rightmost() = z;  
  54.         }  
  55.         else if(y == leftmost())     // 如果y爲最左節點  
  56.             leftmost() = z;          // 維護leftmost(),使它永遠指向最左節點  
  57.     }  
  58.     else  
  59.     {  
  60.         z = create_node(v);        // 產生一個新節點  
  61.         right(y) = z;              // 令新節點成爲插入點之父節點y的右子節點  
  62.         if(y == rightmost())  
  63.             rightmost() = z;       // 維護rightmost(),使它永遠指向最右節點  
  64.     }  
  65.     parent(z) = y;      // 設定新節點的父節點  
  66.     left(z) = 0;        // 設定新節點的左子節點  
  67.     right(z) = 0;       // 設定新節點的右子節點  
  68.     // 新節點的顏色將在_rb_tree_rebalance()設定(並調整)  
  69.     _rb_tree_rebalance(z , header->parent);      // 參數一爲新增節點,參數二爲根節點root  
  70.     ++node_count;       // 節點數累加  
  71.     return iterator(z);  // 返回一個迭代器,指向新增節點  
  72. }  
  73.   
  74.   
  75. // 全局函數  
  76. // 重新令樹形平衡(改變顏色及旋轉樹形)  
  77. // 參數一爲新增節點,參數二爲根節點root  
  78. inline void _rb_tree_rebalance(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
  79. {  
  80.     x->color = _rb_tree_red;    //新節點必爲紅  
  81.     while(x != root && x->parent->color == _rb_tree_red)    // 父節點爲紅  
  82.     {  
  83.         if(x->parent == x->parent->parent->left)      // 父節點爲祖父節點之左子節點  
  84.         {  
  85.             _rb_tree_node_base* y = x->parent->parent->right;    // 令y爲伯父節點  
  86.             if(y && y->color == _rb_tree_red)    // 伯父節點存在,且爲紅  
  87.             {  
  88.                 x->parent->color = _rb_tree_black;           // 更改父節點爲黑色  
  89.                 y->color = _rb_tree_black;                   // 更改伯父節點爲黑色  
  90.                 x->parent->parent->color = _rb_tree_red;     // 更改祖父節點爲紅色  
  91.                 x = x->parent->parent;  
  92.             }  
  93.             else    // 無伯父節點,或伯父節點爲黑色  
  94.             {  
  95.                 if(x == x->parent->right)   // 如果新節點爲父節點之右子節點  
  96.                 {  
  97.                     x = x->parent;  
  98.                     _rb_tree_rotate_left(x , root);    // 第一個參數爲左旋點  
  99.                 }  
  100.                 x->parent->color = _rb_tree_black;     // 改變顏色  
  101.                 x->parent->parent->color = _rb_tree_red;  
  102.                 _rb_tree_rotate_right(x->parent->parent , root);    // 第一個參數爲右旋點  
  103.             }  
  104.         }  
  105.         else          // 父節點爲祖父節點之右子節點  
  106.         {  
  107.             _rb_tree_node_base* y = x->parent->parent->left;    // 令y爲伯父節點  
  108.             if(y && y->color == _rb_tree_red)    // 有伯父節點,且爲紅  
  109.             {  
  110.                 x->parent->color = _rb_tree_black;           // 更改父節點爲黑色  
  111.                 y->color = _rb_tree_black;                   // 更改伯父節點爲黑色  
  112.                 x->parent->parent->color = _rb_tree_red;     // 更改祖父節點爲紅色  
  113.                 x = x->parent->parent;          // 準備繼續往上層檢查  
  114.             }  
  115.             else    // 無伯父節點,或伯父節點爲黑色  
  116.             {  
  117.                 if(x == x->parent->left)        // 如果新節點爲父節點之左子節點  
  118.                 {  
  119.                     x = x->parent;  
  120.                     _rb_tree_rotate_right(x , root);    // 第一個參數爲右旋點  
  121.                 }  
  122.                 x->parent->color = _rb_tree_black;     // 改變顏色  
  123.                 x->parent->parent->color = _rb_tree_red;  
  124.                 _rb_tree_rotate_left(x->parent->parent , root);    // 第一個參數爲左旋點  
  125.             }  
  126.         }  
  127.     }//while  
  128.     root->color = _rb_tree_black;    // 根節點永遠爲黑色  
  129. }  
  130.   
  131.   
  132. // 左旋函數  
  133. inline void _rb_tree_rotate_left(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
  134. {  
  135.     // x 爲旋轉點  
  136.     _rb_tree_node_base* y = x->right;          // 令y爲旋轉點的右子節點  
  137.     x->right = y->left;  
  138.     if(y->left != 0)  
  139.         y->left->parent = x;           // 別忘了回馬槍設定父節點  
  140.     y->parent = x->parent;  
  141.   
  142.     // 令y完全頂替x的地位(必須將x對其父節點的關係完全接收過來)  
  143.     if(x == root)    // x爲根節點  
  144.         root = y;  
  145.     else if(x == x->parent->left)         // x爲其父節點的左子節點  
  146.         x->parent->left = y;  
  147.     else                                  // x爲其父節點的右子節點  
  148.         x->parent->right = y;  
  149.     y->left = x;  
  150.     x->parent = y;  
  151. }  
  152.   
  153.   
  154. // 右旋函數  
  155. inline void _rb_tree_rotate_right(_rb_tree_node_base* x , _rb_tree_node_base*& root)  
  156. {  
  157.     // x 爲旋轉點  
  158.     _rb_tree_node_base* y = x->left;          // 令y爲旋轉點的左子節點  
  159.     x->left = y->right;  
  160.     if(y->right != 0)  
  161.         y->right->parent = x;           // 別忘了回馬槍設定父節點  
  162.     y->parent = x->parent;  
  163.   
  164.     // 令y完全頂替x的地位(必須將x對其父節點的關係完全接收過來)  
  165.     if(x == root)  
  166.         root = y;  
  167.     else if(x == x->parent->right)         // x爲其父節點的右子節點  
  168.         x->parent->right = y;  
  169.     else                                  // x爲其父節點的左子節點  
  170.         x->parent->left = y;  
  171.     y->right = x;  
  172.     x->parent = y;  
  173. }  


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