目錄
一、紅黑樹概述
二、紅黑樹節點設計
? (1)枚舉紅黑
(2)紅黑樹的節點設計
三、紅黑樹核心實現:Insert
1.首先將節點遍歷到對應位置創建對應節點并插入到二叉搜索樹對應的位置
2.本文重點的重點
(1)parent為黑時直接插入即可
(2)uncle存在且為紅
(3)uncle不存在
(4)uncle存在且為黑
四、紅黑樹檢查代碼
五、總代碼
1.RBTree.h
2.Test.c代碼
一、紅黑樹概述
學完AVL樹之后對我來說,紅黑樹可能更容易理解,還有水了這么多篇,該認真寫一篇了,哈哈哈哈。
紅黑樹顧名思義就是得有紅色和黑色的樹,紅黑樹利用紅色和黑色節點來創建二叉平衡搜索樹
紅黑樹的5條核心特點:
(1)每個節點是紅色或黑色
(2)根節點必須是黑色
(3)所有葉子節點(NIL/空節點)是黑色(NIL節點是紅黑樹中所有空指針的替代節點,表示樹的
葉子位置。)
(4)紅色節點的子節點必須是黑色(即不能有連續的黑色節點)
(5)從任意節點到其所有葉子節點的路徑上,黑色節點的數量相同
二、紅黑樹節點設計
? (1)枚舉紅黑
enum Colour
{RED,BLACK
};
(2)紅黑樹的節點設計
1.其中的三叉鏈是指_left _right _parent ,在我的理解中_left和_right是為了前進,而_parent是為了后退
2.kv是儲存的值,set中儲存的V是與K一樣的類型,map中儲存的是<const K ,V>
template<class K,class V>
struct RBTreeNode
{//三叉鏈RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K,V> & kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}
};
三、紅黑樹核心實現:Insert
二叉樹的核心操作就是插入,插入分為三種情況:
(1)uncle存在且為紅
(2)uncle不存在
(3)uncle存在且為黑
1.首先將節點遍歷到對應位置創建對應節點并插入到二叉搜索樹對應的位置
typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){//意味著這里是一顆空樹,直接插入就行if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;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;}}//這里是創建一個初始化_kv(kv)的新節點,并別把顏色初始化為紅色cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else if (parent->_kv.first > kv.first){parent->_left = cur;}cur->_parent = parent;
2.本文重點的重點
注:給出的圖畫是以 parent == grandfather->_left 條件下進行的,panret==grandfather->_right同理,最后有完整代碼可參考
(1)parent為黑時直接插入即可
!!!下面都是parent為紅的情況
(2)uncle存在且為紅
在parent和uncle為紅的情況我們,讓我們grandfatehr變成紅,讓parent和uncle變成黑
然后讓cur=grandfatehr ,parent=cur->_parent,grandfather=parent->_parent
繼續向上處理:
(一)?.grandfather沒有父親,就是根,grandfatehr變黑即可
(二).grandfather有父親,父親時黑色,就結束了
(三).grandfather有父親,父親是紅色,和上面一樣處理
繼續像剛才一樣的操作
因為grandfatehr 是根節點,因為根節點只能是紅,最后讓根節點的顏色變成紅? ? ? ? ? ? ? ? ??
最后有完整代碼,現在給出對應部分代碼
Node* uncle = grandfather->_right;
//uncle存在且為紅
if (uncle && uncle->_col == RED)
{//變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//繼續向上處理cur = grandfather;parent = cur->_parent;//grandfather = parent->_parent;
}
(3)uncle不存在
這里是grandfather->_left且parent是紅色節點的前提,這里有兩種情況
情況一:parent->_left=cur
在grandfather節點進行右旋,讓grandfather的顏色變成紅色,讓parent的顏色變成黑色
情況二:parent->_right=cur
在parent節點進行左旋
再在grandfather哪里進行右旋
旋轉完之后,grandfather的顏色變紅,cur的顏色變黑
代碼:
現在給出對應部分代碼,左旋,右旋,左右旋,右左旋代碼也在最后的完整代碼中
else//uncle不存在或uncle存在且為黑
{//這是什么意思?這里我自己判斷了if (cur == parent->_left ){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED; }break;
}
(4)uncle存在且為黑
這里是grandfather->_right且parent是紅色節點的前提,與(3)的以grandfather->_left情況相反了
這種情況有點特殊,要操作一步才能得到
按照(2)變成,變成我們(4)所需要的狀態
情況一:parent->_right=cur
然后對grandfather進行左旋
grandfather的顏色變紅,parent的顏色變黑
情況二:parent->_left=cur
首先要在parent進行右旋操作
再對grandparent進行左旋,讓grandfather的顏色變紅,cur的顏色變黑
代碼:
現在給出對應部分代碼,左旋,右旋,左右旋,右左旋代碼也在最后的完整代碼中
if (cur == parent->_right)
{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;
}
else
{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;
}
break;
四、紅黑樹檢查代碼
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;
}
2.根據紅節點的父親是否是紅色來判斷紅黑樹是否有誤
//為什么不是int & blacknum,因為要利用棧幀,保存當前形式下,blackanum的值
bool CheckColour(Node* root, int blacknum,int benchmark)
{if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent &&root->_parent->_col == RED){cout << root->_kv.first << "出現連續紅色節點" << endl;return false;}return CheckColour(root->_left,blacknum,benchmark)&& CheckColour(root->_right, blacknum, benchmark);
}
3.利用左邊的那條支路創建黑高的基準值,再在checkcolour把所有節點黑高節點檢查一遍得出結果
bool IsBalance()
{return IsBalance(_root);
}
bool IsBalance(Node* root)
{if (root == nullptr)return true;if (root->_col != BLACK)return false;
// return CheckColour(root);//基準值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++benchmark; }//這里是以最左路徑為標準cur = cur->_left;}return CheckColour(root, 0, benchmark);
}
五、總代碼
1.RBTree.h
#pragma once
#include<iostream>
#include<stdio.h>
using namespace std;enum Colour
{RED,BLACK
};
template<class K,class V>
struct RBTreeNode
{//三叉鏈RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K,V> & kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}
};template<class K, class V>
struct RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){//Node* parent = 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;}else if (parent->_kv.first > kv.first){parent->_left = cur;}cur->_parent = parent;while (parent&&parent->_col==RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){Node* uncle = grandfather->_right;//uncle存在且為紅if (uncle && uncle->_col == RED){//變色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//繼續向上處理cur = grandfather;parent = cur->_parent;//grandfather = parent->_parent; }else//uncle不存在或uncle存在且為黑{//這是什么意思?這里我自己判斷了if (cur == parent->_left ){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED; }break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;//grandfather = parent->_parent;}else{//我覺得案按理來說得判斷,這是叔叔不存在的情況?if (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;//parent->_col = RED;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){//這是AVL樹第一集的末尾了Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}void RotateR(Node* parent){//這是AVL樹第一集的末尾了Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}Node* ppnode = parent->_parent;cur->_right = parent;//Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_right == parent){ppnode->_right = cur;}else{ppnode->_left = cur;}cur->_parent = ppnode;}}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;//int bf = cur->_left->_bf;RotateR(parent->_right);RotateL(parent);}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;// int bf = curright->_bf;RotateL(parent->_left);RotateR(parent);}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;}//為什么不是int & blacknum,因為要利用棧幀,保存當前形式下,blackanum的值bool CheckColour(Node* root, int blacknum,int benchmark){if (root == nullptr){if (blacknum != benchmark)return false;return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent &&root->_parent->_col == RED){cout << root->_kv.first << "出現連續紅色節點" << endl;return false;}return CheckColour(root->_left,blacknum,benchmark)&& CheckColour(root->_right, blacknum, benchmark);}bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr)return true;if (root->_col != BLACK)return false;// return CheckColour(root);//基準值int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++benchmark; }//這里是以最左路徑為標準cur = cur->_left;}return CheckColour(root, 0, benchmark);}
private:Node* _root = nullptr;
};
2.Test.c代碼
插入10000個隨機數進行測試
#include"RBTree.h"
#include<vector>
#include<time.h>
//int main()
//{
// int a[] = { 4,2,6,1,3,5,15,7,16,14 };
// RBTree<int, int> t;
// for (auto e : a)
// {
// t.Insert(make_pair(e, e));
// cout << "Insert:" << e << "->"<<t.IsBalance() << endl;
// }
// return 0;
//}int main()
{const int N = 10000;vector<int> v;v.reserve(N);srand(time(0));for (int i = 0;i < N;i++){v.push_back(rand());}RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));cout << "Insert:" << e << "->" << t.IsBalance() << endl;}return 0;
}
謝謝觀眾老爺的觀看!!!