轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7740956
一、紅黑樹概述
紅黑樹和我們以前學過的AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能。不過自從紅黑樹出來後,AVL樹就被放到了博物館裏,據說是紅黑樹有更好的效率,更高的統計性能。這一點在我們瞭解了紅黑樹的實現原理後,就會有更加深切的體會。紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。學過數據結構的人應該都已經領教過AVL樹的複雜,但AVL樹的複雜比起紅黑樹來說簡直是小巫見大巫,紅黑樹纔是真正的變態級數據結構。
由於STL中的關聯式容器默認的底層實現都是紅黑樹,因此紅黑樹對於後續學習STL源碼還是很重要的,有必要掌握紅黑樹的實現原理和源碼實現。
紅黑樹是AVL樹的變種,紅黑樹通過一些着色法則確保沒有一條路徑會比其它路徑長出兩倍,因而達到接近平衡的目的。所謂紅黑樹,不僅是一個二叉搜索樹,而且必須滿足一下規則:
1、每個節點不是紅色就是黑色。
2、根節點爲黑色。
3、如果節點爲紅色,其子節點必須爲黑色。
4、任意一個節點到到NULL(樹尾端)的任何路徑,所含之黑色節點數必須相同。
上面的這些約束保證了這個樹大致上是平衡的,這也決定了紅黑樹的插入、刪除、查詢等操作是比較快速的。 根據規則4,新增節點必須爲紅色;根據規則3,新增節點之父節點必須爲黑色。當新增節點根據二叉搜索樹的規則到達其插入點時,卻未能符合上述條件時,就必須調整顏色並旋轉樹形,如下圖:
二、紅黑樹上結點的插入
在討論紅黑樹的插入操作之前必須要明白,任何一個即將插入的新結點的初始顏色都爲紅色。這一點很容易理解,因爲插入黑點會增加某條路徑上黑結點的數目,從而導致整棵樹黑高度的不平衡。但如果新結點的父結點爲紅色時(如下圖所示),將會違反紅黑樹的性質:一條路徑上不能出現相鄰的兩個紅色結點。這時就需要通過一系列操作來使紅黑樹保持平衡。1、黑父
如下圖所示,如果新節點的父結點爲黑色結點,那麼插入一個紅點將不會影響紅黑樹的平衡,此時插入操作完成。紅黑樹比AVL樹優秀的地方之一在於黑父的情況比較常見,從而使紅黑樹需要旋轉的機率相對AVL樹來說會少一些。
如果新節點的父結點爲紅色,這時就需要進行一系列操作以保證整棵樹紅黑性質。如下圖所示,由於父結點爲紅色,此時可以判定,祖父結點必定爲黑色。這時需要根據叔父結點的顏色來決定做什麼樣的操作。青色結點表示顏色未知。由於有可能需要根結點到新點的路徑上進行多次旋轉操作,而每次進行不平衡判斷的起始點(我們可將其視爲新點)都不一樣。所以我們在此使用一個藍色箭頭指向這個起始點,並稱之爲判定點。
當叔父結點爲紅色時,如下圖所示,無需進行旋轉操作,只要將父和叔結點變爲黑色,將祖父結點變爲紅色即可。但由於祖父結點的父結點有可能爲紅色,從而違反紅黑樹性質。此時必須將祖父結點作爲新的判定點繼續向上(迭代)進行平衡操作。
2.2 黑叔
當叔父結點爲黑色時,需要進行旋轉,以下圖示了所有的旋轉可能:
Case 1:
Case 4:
其實紅黑樹的插入操作不是很難,甚至比AVL樹的插入操作還更簡單些。紅黑樹的插入操作源代碼如下:
- // 元素插入操作 insert_unique()
- // 插入新值:節點鍵值不允許重複,若重複則插入無效
- // 注意,返回值是個pair,第一個元素是個紅黑樹迭代器,指向新增節點
- // 第二個元素表示插入成功與否
- template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>
- pair<typename rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::iterator , bool>
- rb_tree<Key , Value , KeyOfValue , Compare , Alloc>::insert_unique(const Value &v)
- {
- rb_tree_node* y = header; // 根節點root的父節點
- rb_tree_node* x = root(); // 從根節點開始
- bool comp = true;
- while(x != 0)
- {
- y = x;
- comp = key_compare(KeyOfValue()(v) , key(x)); // v鍵值小於目前節點之鍵值?
- x = comp ? left(x) : right(x); // 遇“大”則往左,遇“小於或等於”則往右
- }
- // 離開while循環之後,y所指即插入點之父節點(此時的它必爲葉節點)
- iterator j = iterator(y); // 令迭代器j指向插入點之父節點y
- if(comp) // 如果離開while循環時comp爲真(表示遇“大”,將插入於左側)
- {
- if(j == begin()) // 如果插入點之父節點爲最左節點
- return pair<iterator , bool>(_insert(x , y , z) , true);
- else // 否則(插入點之父節點不爲最左節點)
- --j; // 調整j,回頭準備測試
- }
- if(key_compare(key(j.node) , KeyOfValue()(v) ))
- // 新鍵值不與既有節點之鍵值重複,於是以下執行安插操作
- return pair<iterator , bool>(_insert(x , y , z) , true);
- // 以上,x爲新值插入點,y爲插入點之父節點,v爲新值
- // 進行至此,表示新值一定與樹中鍵值重複,那麼就不應該插入新值
- return pair<iterator , bool>(j , false);
- }
- // 真正地插入執行程序 _insert()
- template<class Key , class Value , class KeyOfValue , class Compare , class Alloc>
- typename<Key , Value , KeyOfValue , Compare , Alloc>::_insert(base_ptr x_ , base_ptr y_ , const Value &v)
- {
- // 參數x_ 爲新值插入點,參數y_爲插入點之父節點,參數v爲新值
- link_type x = (link_type) x_;
- link_type y = (link_type) y_;
- link_type z;
- // key_compare 是鍵值大小比較準則。應該會是個function object
- if(y == header || x != 0 || key_compare(KeyOfValue()(v) , key(y) ))
- {
- z = create_node(v); // 產生一個新節點
- left(y) = z; // 這使得當y即爲header時,leftmost() = z
- if(y == header)
- {
- root() = z;
- rightmost() = z;
- }
- else if(y == leftmost()) // 如果y爲最左節點
- leftmost() = z; // 維護leftmost(),使它永遠指向最左節點
- }
- else
- {
- z = create_node(v); // 產生一個新節點
- right(y) = z; // 令新節點成爲插入點之父節點y的右子節點
- if(y == rightmost())
- rightmost() = z; // 維護rightmost(),使它永遠指向最右節點
- }
- parent(z) = y; // 設定新節點的父節點
- left(z) = 0; // 設定新節點的左子節點
- right(z) = 0; // 設定新節點的右子節點
- // 新節點的顏色將在_rb_tree_rebalance()設定(並調整)
- _rb_tree_rebalance(z , header->parent); // 參數一爲新增節點,參數二爲根節點root
- ++node_count; // 節點數累加
- return iterator(z); // 返回一個迭代器,指向新增節點
- }
- // 全局函數
- // 重新令樹形平衡(改變顏色及旋轉樹形)
- // 參數一爲新增節點,參數二爲根節點root
- inline void _rb_tree_rebalance(_rb_tree_node_base* x , _rb_tree_node_base*& root)
- {
- x->color = _rb_tree_red; //新節點必爲紅
- while(x != root && x->parent->color == _rb_tree_red) // 父節點爲紅
- {
- if(x->parent == x->parent->parent->left) // 父節點爲祖父節點之左子節點
- {
- _rb_tree_node_base* y = x->parent->parent->right; // 令y爲伯父節點
- if(y && y->color == _rb_tree_red) // 伯父節點存在,且爲紅
- {
- x->parent->color = _rb_tree_black; // 更改父節點爲黑色
- y->color = _rb_tree_black; // 更改伯父節點爲黑色
- x->parent->parent->color = _rb_tree_red; // 更改祖父節點爲紅色
- x = x->parent->parent;
- }
- else // 無伯父節點,或伯父節點爲黑色
- {
- if(x == x->parent->right) // 如果新節點爲父節點之右子節點
- {
- x = x->parent;
- _rb_tree_rotate_left(x , root); // 第一個參數爲左旋點
- }
- x->parent->color = _rb_tree_black; // 改變顏色
- x->parent->parent->color = _rb_tree_red;
- _rb_tree_rotate_right(x->parent->parent , root); // 第一個參數爲右旋點
- }
- }
- else // 父節點爲祖父節點之右子節點
- {
- _rb_tree_node_base* y = x->parent->parent->left; // 令y爲伯父節點
- if(y && y->color == _rb_tree_red) // 有伯父節點,且爲紅
- {
- x->parent->color = _rb_tree_black; // 更改父節點爲黑色
- y->color = _rb_tree_black; // 更改伯父節點爲黑色
- x->parent->parent->color = _rb_tree_red; // 更改祖父節點爲紅色
- x = x->parent->parent; // 準備繼續往上層檢查
- }
- else // 無伯父節點,或伯父節點爲黑色
- {
- if(x == x->parent->left) // 如果新節點爲父節點之左子節點
- {
- x = x->parent;
- _rb_tree_rotate_right(x , root); // 第一個參數爲右旋點
- }
- x->parent->color = _rb_tree_black; // 改變顏色
- x->parent->parent->color = _rb_tree_red;
- _rb_tree_rotate_left(x->parent->parent , root); // 第一個參數爲左旋點
- }
- }
- }//while
- root->color = _rb_tree_black; // 根節點永遠爲黑色
- }
- // 左旋函數
- inline void _rb_tree_rotate_left(_rb_tree_node_base* x , _rb_tree_node_base*& root)
- {
- // x 爲旋轉點
- _rb_tree_node_base* y = x->right; // 令y爲旋轉點的右子節點
- x->right = y->left;
- if(y->left != 0)
- y->left->parent = x; // 別忘了回馬槍設定父節點
- y->parent = x->parent;
- // 令y完全頂替x的地位(必須將x對其父節點的關係完全接收過來)
- if(x == root) // x爲根節點
- root = y;
- else if(x == x->parent->left) // x爲其父節點的左子節點
- x->parent->left = y;
- else // x爲其父節點的右子節點
- x->parent->right = y;
- y->left = x;
- x->parent = y;
- }
- // 右旋函數
- inline void _rb_tree_rotate_right(_rb_tree_node_base* x , _rb_tree_node_base*& root)
- {
- // x 爲旋轉點
- _rb_tree_node_base* y = x->left; // 令y爲旋轉點的左子節點
- x->left = y->right;
- if(y->right != 0)
- y->right->parent = x; // 別忘了回馬槍設定父節點
- y->parent = x->parent;
- // 令y完全頂替x的地位(必須將x對其父節點的關係完全接收過來)
- if(x == root)
- root = y;
- else if(x == x->parent->right) // x爲其父節點的右子節點
- x->parent->right = y;
- else // x爲其父節點的左子節點
- x->parent->left = y;
- y->right = x;
- x->parent = y;
- }