红黑树每个节点新增了一个存储位(成员变量)表示结点的颜色,并且红黑树并不像AVL树一样是保持严格的平衡的,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍。AVL树是最平衡的(非常严格) ,因为它两的高度差不超过1, 。
红黑树性质:
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的:保证了不能出现连续的红色节点。
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 :保证每条路径上都有相同数量的黑色节点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
这棵树的最短路径就是全黑,最长路径就是一黑一红的红黑相间。
通过上面的性质约束来达到最长路径无法超过最短路径的2倍,虽然并不是严格平衡的,但是也可以达到近似的平衡,树的高度比AVL树略高一些,完全可控,优势在于旋转的更少,性能却差不太多。单纯的比较增删查改的效率,红黑树是无法超过AVL树,。
假设全部的黑色节点有N个
最短路径长度:logN
整棵树的节点数量:[N,2N]之间
最长路径长度:2logN
单论查找,AVL树很接近于完全二叉树的,很接近logNormal。是优于红黑树的,但是这个量级不大,但是综合而言 红黑树其实更胜一筹,红黑树旋转的更少,AVL树的严格平衡是通过付出旋转大量的代价来达到的。
节点构建
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)
{}
};
//为了封装map和set,做如下修改。
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)//默认是红色节点,插入黑色节点是一定有问题的(因为要求每条路径都要有相同数量的黑色节点) 但是红色节点是不一定的。
{}
};
节点插入(关键)
当我们插入一个新节点的时候,该节点的颜色到底是什么?取决于我们更倾向于违反那一条性质?
我们更愿意该节点是红色,因为如果是黑的话一定会违反规则4,而且是影响到了每一条路径。而对于红色节点,如果插入到的是红色节点的左右孩子之一,则是违反了规则三,而且仅仅只是影响到了局部,但是也存在可能什么都没有违反也就是插入到黑节点的下面。所以我们创立结点的时候我们默认该节点是红色。
当我们是红色节点cur插入的时候,如果违法了规则,可以确定父亲节点一定是红色,而爷爷节点一定存在且是黑色。
bool Insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; return true; } KeyOfT kot; Node* parent = nullptr; Node* cur = _root; while (cur)//寻找插入节点的位置 { if (kot(cur->_data) < kot(data)) { parent = cur; cur = cur->_right; } else if (kot(cur->_data) > kot(data)) { parent = cur; cur = cur->_left; } else { return false; } } //开始插入节点 cur = new Node(data); if (kot(parent->_data) > kot(data)) { parent->_left = cur; } else { parent->_right = cur; } cur->_parent = parent; while (parent && parent->_col == RED)//插入节点的位置其父节点是红色便需要开始调整,此时grandfather一定是黑色的。 { Node* grandfather = parent->_parent;//此时parent一定要变成黑,而不是cur变成黑(因为我们插入的就是红节点,这是一个原则)。此时一个比较关键的节点是uncle节点,uncle节点有几种状态。 而找uncle之前得先找到grandfather。 if (grandfather->_left == parent) { Node* uncle = grandfather->_right; // 情况1:uncle存在且为红,uncle变成黑,grandfather变成红(因为parent和uncle变黑增加了该条路径下黑色节点的路径,所以grandfather变红使得该条路经下的黑色节点数量并未发生变化),但是由于grandfather由黑变成了红,我们需要考虑old_grandfather的情况,此时需要继续往上处理,也就是将grandfather当成cur也就是新增节点,计算old_grandfather的parent、uncle、grandfather的情况。 if (uncle && uncle->_col == RED) { parent->_col = BLACK;//父亲变黑 uncle->_col = BLACK;//叔叔变黑 grandfather->_col = RED;//祖父变红 // 将grandfather看成cur,继续往上调整 cur = grandfather;//把祖父给给cur parent = cur->_parent;//跟新父亲 } else // 情况2+3:u不存在/u存在且为黑,旋转+变色。注意uncle存在且为黑一定是从其它情况变过来的。现在我们需要关注他是哪一种旋转。 { // g // p u // c //单旋 if (cur == parent->_left) { RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g // p u // c //双旋 RotateL(parent); RotateR(grandfather); cur->_col = BLACK; //parent->_col = RED; grandfather->_col = RED; } break;//情况2+3不需要再向上处理了 } } else // (grandfather->_right == parent) { // g // u p // c Node* uncle = grandfather->_left; // 情况1:u存在且为红,变色处理,并继续往上处理 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; // 继续往上调整 cur = grandfather; parent = cur->_parent; } else // 情况2+3:u不存在/u存在且为黑,旋转+变色 { // g // u p // c if (cur == parent->_right) { RotateL(grandfather); grandfather->_col = RED; parent->_col = BLACK; } else { // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK;//跳出上述循环的原因可能是父亲不存在,而cur一定是根,跟有可能已经被我们调整成为红色呢,所以这边上一道保险,不管什么情况都要讲根变成黑色。 return true; }
情况1:插入的第一个节点就是根节点,将其变成黑的
情况2:插入的节点其父亲是黑的,就不用额外处理,直接插入就可以了。
情况3:cur为红,父亲为红,爷爷为黑,叔叔存在且为为红(红节点新增关键在于叔叔的状态)
解决方式:不能让cur变黑,因为cur默认的构造是红色的。将父亲和叔叔改成黑(该路径就多了一个黑节点,但是可以让cur合理插入进来),将爷爷改为红,减少一个黑节点,这样就避免了由于叔父变黑导致的这条路径相比其它路径要多一个黑节点的问题。继续向上调整。而且这种变色并不需要关注形状
情况4:cur为红,父亲节点为红,爷爷为黑,叔叔不存在或者叔叔存在且为黑。
需要注意叔叔存在且为黑这种情况不会是直接存在的,像下面一样,因为这样的话从g节点到每个叶节点的黑色节点的数量并不统一。
所以这种情况一般是:叔叔存在且为黑这种情况一般是下面子树新增导致,变色演变过来 的,如下:
处理方式:旋转、
bool Insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; return true; } KeyOfT kot; Node* parent = nullptr; Node* cur = _root; while (cur) { if (kot(cur->_data) < kot(data)) { parent = cur; cur = cur->_right; } else if (kot(cur->_data) > kot(data)) { parent = cur; cur = cur->_left; } else { return false; } } cur = new Node(data); if (kot(parent->_data) > kot(data)) { parent->_left = cur; } else { parent->_right = cur; } cur->_parent = parent; while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (grandfather->_left == parent) { Node* uncle = grandfather->_right; // 情况1:u存在且为红,变色处理,并继续往上处理 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; // 继续往上调整 cur = grandfather; parent = cur->_parent; } else // 情况2+3:u不存在/u存在且为黑,旋转+变色 { // g // p u // c if (cur == parent->_left) { RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; //parent->_col = RED; grandfather->_col = RED; } break; } } else // (grandfather->_right == parent) { // g // u p // c Node* uncle = grandfather->_left; // 情况1:u存在且为红,变色处理,并继续往上处理 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; // 继续往上调整 cur = grandfather; parent = cur->_parent; } else // 情况2+3:u不存在/u存在且为黑,旋转+变色 { // g // u p // c if (cur == parent->_right) { RotateL(grandfather); grandfather->_col = RED; parent->_col = BLACK; } else { // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; }
IsBalance函数
//不能通过最长路径是否是最短路径的两倍来判断,而应该从红黑树的五条性质入手,满足这五条性质自然就能满足最长路径中节点个数不会超过最短路径节点个数的两倍。
bool IsBalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;//统计其中一条路径黑色节点的数量做为基准值
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)//红色节点一定由父亲
{
cout << "存在连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
析构函数
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
Find函数
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
RBTree
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>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
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 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;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void InOrder()
{
_InOrder(_root);
}
bool IsBalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};