目錄
前言:
一,紅黑樹的概念
1.1,紅黑樹的規則
1.2,紅黑樹的最長路徑?
1.3,紅黑樹的效率分析
?二,紅黑樹的實現
2.1,紅黑樹的結構
2.2,紅黑樹的插入
2.2.1,大致過程?
2.2.2,情況1:變色處理?
?2.2.3,情況2:單旋+變色
2.2.4,情況3:雙旋+變色
2.3,紅黑樹插入代碼的實現
2.4,紅黑樹的驗證
三,整體代碼
四,測試代碼
前言:
本篇會用到上篇【AVL樹的實現】中的旋轉知識。
一,紅黑樹的概念
????????紅黑樹是一顆二叉搜索樹,它的每一個節點增加一個存儲為來表示節點的顏色。可以是紅色或者黑色。它通過對從根開始到葉子節點的每條路徑上各個節點顏色的約束,確保最長路徑不會超過最短路徑的2倍,從而實現平衡的。
1.1,紅黑樹的規則
1,每個節點不是紅色就是黑色。
2,根節點是黑色的。
3,紅色節點的兩個孩子只能是 黑色節點或者是空節點。也就是說不能出現連續的紅色節點
4,對于任意一個節點,從該節點開始,到葉子節點的所有路徑上,均包含相同數量的黑色節點。
以上 都是紅黑樹,滿足紅黑樹的規則。
1.2,紅黑樹的最長路徑?
1,由第四條規則可知,從根節點開始的每條路徑上,黑色節點的數量相同。所以在極端場景下,一顆紅黑樹,它的最短路徑就是節點全為黑色的路徑。假設紅黑樹的每條路徑黑色節點數量都為b,那么最短路徑的節點數量為b.
2,由 第三條規則可知,一條路徑上不能由連續的紅色節點,最長路徑是由一黑一紅間隔組成的,所以最長路徑為2*b。
3,而對于一顆紅黑樹,最長和最短路徑不一定存在。我們可以得出對于任意一顆紅黑樹,它的任意 一條路徑長度x都是,b<=x<=2*b.
1.3,紅黑樹的效率分析
假設N是紅黑樹節點的數量,h是最短路徑的長度,最長路徑不超過2*h
可以得到2^h-1<= N <= 2^(2*h)-1,推出h大致為logN,也就意味著紅黑樹的增刪查該最壞走2*logN,時間復雜度O(logN).
?二,紅黑樹的實現
2.1,紅黑樹的結構
enum color
{
?? ?Red,
?? ?Black
};template<class k,class v>
struct RBTreeNode
{
?? ?RBTreeNode(const pair<k,v>& kv)
?? ??? ?:_left(nullptr)
?? ??? ?,_right(nullptr)
?? ??? ?,_parent(nullptr)
?? ??? ?,_kv(kv)
?? ?{}
?? ?RBTreeNode<k, v>* _left;
?? ?RBTreeNode<k, v>* _right;
?? ?RBTreeNode<k, v>* _parent;
?? ?pair<k, v> _kv;
?? ?color _col;
};template<class k,class v>
class RBTree
{
?? ?typedef RBTreeNode<k, v> Node;
public:? ?//...
private:
?? ?Node* _root=nullptr;
};
?
2.2,紅黑樹的插入
2.2.1,大致過程?
1,插入一個值需要按照搜索樹的規則進行插入,再判斷插入后是否滿足紅黑樹的規則。
2,如果是空樹插入,新增節點就是黑色節點。如果是非空樹插入,新增節點就必須是紅色節點,因為如果插入黑色節點,就一定會破壞規則4,而插入紅色節點是有可能會破壞規則3,而且對于規則3來說,解決方法比規則4的解決方法容易。
3,非空樹插入后,如果父節點是黑色節點,則沒有違反任何規則,插入結束。
4,非空樹插入后,如果父節點是紅色節點,則違反規則3,進一步分析。
2.2.2,情況1:變色處理?
由上圖可知,c為紅,p為紅,g為黑,u存在且為紅。在這種情況下,我們需要將p和u變黑,g變紅,g成為新的c,繼續向上更新。
分析:
????????因為p和u都是紅色的,g是黑色的。把p和u變黑,左邊子樹路徑各增加一個黑色節點,g再變紅,相當于g所在路徑的黑色節點數量不變,同時解決了c和p連續紅節點的問題。
????????需要繼續往上跟新是因為g是紅色,如果g的父親還是紅色,就需要繼續處理;如果g的父親是黑色,則處理結束;如果g是整棵樹的根,再將g變成黑色即可。?
?2.2.3,情況2:單旋+變色
c為紅,p為紅,u不存在或者u存在且u為黑色。在這種情況下,就需要進行單旋+變色處理
分析:
u不存在時,c一定是新增節點。
u存在且為黑色時,c一定不是新增節點,是在c的子樹中插入,符號情況1,經過情況1后,c被調整為紅色的。
?上圖展示的是右單旋的場景,下面是根據右單旋,畫出的左單旋大致圖,與右單旋過程相似:
?總結:經過單旋+變色后,我們可以看到p做了子樹新的根,且p是黑色的,所以不管p的父親是黑色的還是紅色的,都滿足紅黑樹的規則,所以此時,就不需要往上更新了。
2.2.4,情況3:雙旋+變色
c為紅,p為紅,g為黑,u不存在或者u存在且為黑,u不存在,則c一定是新增結點,u存在且為黑,則 c一定不是新增,c之前是黑色的,是在c的子樹中插入,符合情況1,變色將c從黑色變成紅色,更新上來的。
?上圖展示的是左右雙旋+變色,同樣右左雙旋類似:
同樣經過雙旋+變色后,c成為新的根,且c為黑色,所以也不需要繼續向上更新了。
2.3,紅黑樹插入代碼的實現
bool Insert(const pair<k, v>& kv)
{//插入根節點,color->Blackif (_root == nullptr){_root = new Node(kv);_root->_col = Black;return true;}//根據搜索樹的規則查找插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur =cur->_left;}else{return false;}}//插入,新節點的顏色為紅色cur = new Node(kv);cur->_col = Red;//與父親節點連接好if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;//顏色處理+旋轉while (parent&& parent->_col == Red){//gNode* grandfather = parent->_parent;if (parent == grandfather->_left){//p為g的左邊// g// p uNode* uncle = grandfather->_right;//叔叔存在且為紅,情況1if (uncle && uncle->_col == Red){//變色parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;//繼續向上處理cur = grandfather;parent = cur->_parent;}else{//情況2,3//叔叔不存在或者叔叔為黑//u為黑,則c之前是黑的//u不存在,則c是新插入的if (cur == parent->_left){//右單旋場景// g// p u// cRotateR(grandfather);//變色parent->_col = Black;grandfather->_col = Red;}else{//左右雙旋場景// g// p u// cRotateL(parent);RotateR(grandfather);//變色cur->_col = Black;grandfather->_col = Red;}//不需要進行向上更新break;}}else{//p為g的右邊// g// u pNode* uncle = grandfather->_left;if (uncle && uncle->_col == Red){//情況1//變色parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{//情況2,3if (cur == parent->_right){//左單旋場景// g// u p// cRotateL(grandfather);//變色parent->_col = Black;grandfather->_col = Red;}else{//右左雙旋場景// g// u p// cRotateR(parent);RotateL(grandfather);//變色cur->_col = Black;grandfather->_col = Red;}//不需要繼續向上更新break;}}}//跟的顏色可能被調整為紅色,最后一步改為黑色即可_root->_col = Black;return true;
}
//右單旋
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;if (subLR)subLR->_parent = parent;parent->_left = subLR;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subL;elsepparent->_right = subL;subL->_parent = pparent;}
}
//左單旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;if (subRL)subRL->_parent = parent;parent->_parent = subR;subR->_left = parent;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subR;elsepparent->_right = subR;subR->_parent = pparent;}
}
2.4,紅黑樹的驗證
我們只需驗證形成的樹是否滿足紅黑樹的四條規則即可。
其中,規則1和規則 2可以直接檢查。
對于規則3,我們可以進行前序遍歷,遍歷到一個節點,判斷該節點的顏色,再判斷它的兩個孩子的顏色,這樣做太麻煩了。我們可以反過來,遍歷到一個節點,如果他是紅色的,判斷它的父親節點是否為黑色。
對于規則4,我們可以先從根開始找到一條路徑上黑色節點的個數refNum,再對整棵樹進行前序遍歷,用變量 blackNum記錄黑色節點的個數,當遍歷到空的時候,與refNum比較即可。
bool Check(Node* root, int BlackNum, int num)
{if (root == nullptr){if (BlackNum != num)return false;return true;}if (root->_col == Red && root->_parent->_col == Red)return false;if (root->_col == Black)BlackNum++;return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);
}
//驗證
bool isbalance()
{if (_root == nullptr)return true;if (_root->_col == Red)return false;int num = 0;Node* cur = _root;while (cur){if (cur->_col == Black)num++;cur = cur->_left;}return Check(_root, 0, num);
}
三,整體代碼
enum color
{Red,Black
};template<class k,class v>
struct RBTreeNode
{RBTreeNode(const pair<k,v>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv){}RBTreeNode<k, v>* _left;RBTreeNode<k, v>* _right;RBTreeNode<k, v>* _parent;pair<k, v> _kv;color _col;
};template<class k,class v>
class RBTree
{typedef RBTreeNode<k, v> Node;
public:bool Insert(const pair<k, v>& kv){//插入根節點,color->Blackif (_root == nullptr){_root = new Node(kv);_root->_col = Black;return true;}//根據搜索樹的規則查找插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur =cur->_left;}else{return false;}}//插入,新節點的顏色為紅色cur = new Node(kv);cur->_col = Red;//與父親節點連接好if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;//顏色處理+旋轉while (parent&& parent->_col == Red){//gNode* grandfather = parent->_parent;if (parent == grandfather->_left){//p為g的左邊// g// p uNode* uncle = grandfather->_right;//叔叔存在且為紅,情況1if (uncle && uncle->_col == Red){//變色parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;//繼續向上處理cur = grandfather;parent = cur->_parent;}else{//情況2,3//叔叔不存在或者叔叔為黑//u為黑,則c之前是黑的//u不存在,則c是新插入的if (cur == parent->_left){//右單旋場景// g// p u// cRotateR(grandfather);//變色parent->_col = Black;grandfather->_col = Red;}else{//左右雙旋場景// g// p u// cRotateL(parent);RotateR(grandfather);//變色cur->_col = Black;grandfather->_col = Red;}//不需要進行向上更新break;}}else{//p為g的右邊// g// u pNode* uncle = grandfather->_left;if (uncle && uncle->_col == Red){//情況1//變色parent->_col = Black;uncle->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{//情況2,3if (cur == parent->_right){//左單旋場景// g// u p// cRotateL(grandfather);//變色parent->_col = Black;grandfather->_col = Red;}else{//右左雙旋場景// g// u p// cRotateR(parent);RotateL(grandfather);//變色cur->_col = Black;grandfather->_col = Red;}//不需要繼續向上更新break;}}}//跟的顏色可能被調整為紅色,最后一步改為黑色即可_root->_col = Black;return true;}//右單旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;if (subLR)subLR->_parent = parent;parent->_left = subLR;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subL;elsepparent->_right = subL;subL->_parent = pparent;}}//左單旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;if (subRL)subRL->_parent = parent;parent->_parent = subR;subR->_left = parent;if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = subR;elsepparent->_right = subR;subR->_parent = pparent;}}void Inorder(){_Inorder(_root);}int Height(){return _Height(_root);}int size(){return _size(_root);}Node* Find(const k& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}//驗證bool isbalance(){if (_root == nullptr)return true;if (_root->_col == Red)return false;int num = 0;Node* cur = _root;while (cur){if (cur->_col == Black)num++;cur = cur->_left;}return Check(_root, 0, num);}
private:bool Check(Node* root, int BlackNum, int num){if (root == nullptr){if (BlackNum != num)return false;return true;}if (root->_col == Red && root->_parent->_col == Red)return false;if (root->_col == Black)BlackNum++;return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);}int _size(Node* root){if (root == nullptr)return 0;return _size(root->_left) + _size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}Node* _root=nullptr;
};
四,測試代碼
void TestRBTree1()
{RBTree<int, int> t;// 常規的測試用例int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// 特殊的帶有雙旋場景的測試用例//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){t.Insert({ e, e });}t.Inorder();cout << t.isbalance() << endl;
}
int main()
{TestRBTree1();return 0;
}