一、紅黑樹
1、定義:紅黑樹是一棵二叉搜索樹,它在每個節點上增加了一個存儲位來表示節點的顏色,可以是Red或Black。通過對任何一條從根到葉子簡單路徑上的顏色來約束,紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似於平衡。
2、性質:
每個節點,非黑即紅;
根節點爲黑色;
如果一個節點是紅色的,則它的2個子節點是黑色的(沒有連續的紅節點);
對每個節點,從該節點到其所有後代葉節點的簡單路徑上,均包含相同數目的黑色節點。(每條路徑的黑色節點的數量相等);
每個葉子節點都是黑色的(這裏的葉子節點是指的NIL節點(空節點));
二、紅黑樹相關
1、紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似於平衡。
如圖所示,插入節點後,爲了保持紅黑樹,最長路徑最多是最短路徑的 2倍。
2、插入節點:
注:cur爲當前節點,parent父節點,grandpa祖父節點,uncle叔叔節點
(1)第一種情況
cur爲紅,parent爲紅,grandpa爲黑,uncle存在且爲紅 --->
則將parent,uncle改爲黑,grandpa改爲紅,然後把grandpa當成cur,繼續向上調整。
(2)第二種情況
cur爲紅,parent爲紅,grandpa爲黑,uncle不存在/uncle爲黑 --->
parent爲grandpa的左孩子,cur爲p的左孩子,則進行右單旋轉;相反,p爲g的右孩子,cur爲p的右孩子,則進行左單旋轉;
parent、grandpa變色--parent變黑,grandpa變紅。
(3)第三種情況
cur爲紅,parent爲紅,grandpa爲黑,uncle不存在/uncle爲黑 -->
parent爲grandpa的左孩子,cur爲parent的右孩子,則針對parent做左單旋轉;相反,parent爲grandpa的右孩子,cur爲p的左孩子,則針對parent做右單旋轉,則轉換成了情況2。
3、判斷紅黑樹:根據紅黑樹的性質進行判定。
4、紅黑樹和AVL樹的比較:
紅黑樹和AVL樹都是高效的平衡二叉樹,增刪查改的時間複雜度都是O(lg(N));
紅黑樹的不追求完全平衡,保證最長路徑不超過最短路徑的2倍,相對而言,降低了旋轉的要求,所以性能跟AVL樹差不多,但是紅黑樹實現更簡單,所以實際運用中紅黑樹更多。
三、相關實現
1、節點
enum Color { RED,BLACK, }; template<class K,class V> struct RBTreeNode { RBTreeNode<K,V> *_left; RBTreeNode<K,V> *_right; RBTreeNode<K,V> *_parent; K _key; V _value; Color _col; RBTreeNode(const K& key,const V& value) :_key(key) ,_value(value) ,_col(RED) ,_left(NULL) ,_right(NULL) ,_parent(NULL) {} };
2、紅黑樹及相關實現
template<class K,class V> class RBTree { typedef RBTreeNode<K,V> Node; public: RBTree() :_root(NULL) {} bool Insert(const K& key,const V& value) { //插入節點 if(_root == NULL) { _root=new Node(key,value); return true; } Node *cur=_root; Node *parent=NULL; while(cur) { if(cur->_key < key) { parent=cur; cur=cur->_right; } else if(cur->_key > key) { parent=cur; cur=cur->_left; } else { return false; } } cur=new Node(key,value); if(parent->_key < key) { parent->_right=new Node(key,value); parent->_right=parent; } else { parent->_left=new Node(key,value); parent->_right=parent; } //更正信息 while(cur != _root && parent->_col == RED) { Node *grandpa=parent->_right; if(grandpa->_left == parent) { Node *uncle=grandpa->_right; if(uncle && uncle->_col == RED) // 第1種情況 { parent->_col=uncle->_col=BLACK; grandpa->_col=RED; //繼續向上調 cur=grandpa; parent=cur->_parent; } else { // 第3種情況:雙旋轉 --> 單旋轉 if(cur == parent->_right) { RotateL(parent);//左單旋 swap(cur,parent); } //第2種情況 parent->_col=BLACK; grandpa->_col=RED; RotateR(grandpa);//右單旋 break; } } else { Node *uncle=grandpa->_left; if(uncle && uncle->_col == RED) // 第1種情況 { parent->_col=uncle->_col=BLACK; grandpa->_col=RED; //繼續向上調 cur=grandpa; parent=cur->_parent; } else { // 第3種情況:雙旋轉 --> 單旋轉 if(cur == parent->_left) { RotateR(parent);//左單旋 swap(cur,parent); } //第2種情況 parent->_col=BLACK; grandpa->_col=RED; RotateR(grandpa);//右單旋 break; } } } return true; } bool IsBlance() { if(_root == NULL) return; if(_root->_col == RED) return false; int k=0; Node *cur=_root; while(cur) { if(cur->_col == BLACK) ++k; cur=cur->_left; } int count=0; return _IsBlance(_root,k,count); } protected: bool _IsBlance(Node *root,const int k,int count) { if(root == NULL) return true; //紅黑樹父子節點顏色不能相同 if(root->_col == RED && root->_parent->_col == RED) { cout<<"錯誤:出現連續紅色節點"<root->_key<<endl; return false; } if(root->_col == BLACK) ++count; //每條路徑中黑色節點數量相等 if(root->_left == NULL && root->_right == NULL && k != count) { cout<<"錯誤:黑色節點數目不等"<<endl; return false; } return _IsBlance(root->_left,k,count) && _IsBlance(root->_right,k,count); } protected: Node *_root; };
3、總結:
紅黑樹也是一種平衡二叉樹,通過存儲節點的顏色紅黑,對其進行處理,使其處於平衡狀態。紅黑樹插入節點時,主要分三種情況,按照不同情況處理。刪除時和AVL樹類似,只是需要注意旋轉及更該節點顏色。