假設我們為上圖分別插入節點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// 元素插入操作 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; } 轉載請標明出處,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7740956新聞熱點
疑難解答