1.红黑树的概念
红黑树是一种二叉搜索树,在每个节点上加一个储存位表示颜色的概念,只有红黑两种颜色,通过对节点颜色的限制,使得红黑树的最长路径不超过最短路径的二倍,因此是接近平衡的。
2.红黑树的性质
1.每个节点只有红和黑两种颜色
2.根节点是黑色的
3.如果一个节点是红色的,则他的两个孩子节点是黑色的
4.对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点
5.每个叶子节点都是黑色的(此处的叶子节点指的是空节点)
3.红黑树节点的定义
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)
{}
};
红黑树多了储存颜色的功能;而且红黑树插入节点是默认插入红色的节点,因为如果插入黑色节点,必定破坏规则4,而插入红节点,可能破坏规则三,可能出现两个连续的红色节点,但是可以修改,也有可能插入红色节点,刚好满足条件,所以我们选择插入默认是红色的节点;
4.红黑树的插入操作
红黑树的插入大致分为两种情况:
情况一:叔叔节点存在,且为红色
这时候,只需要变色就可以了
p和u节点变成黑色,g节点变成红色,然后继续向上更新,直到根节点
情况二:叔叔节点不存在,或者叔叔节点存在但是为黑色
这个时候需要变色+旋转
我们还需要考虑cur是在parent的左边还是右边,旋转的方向不同
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);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//调整颜色
//parent不存在,或者parent颜色为黑,跳出循环
while (parent && parent->_col == RED)
{
//关键看叔叔
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//叔叔存在且为红
// 变色即可
if (uncle && uncle->_col==RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续向上更新
cur = grandfather;
parent = cur->_parent;
}
else //叔叔不存在或者叔叔为黑
{
if (cur == parent->_left)
{
//右旋
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//左右旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
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;
}
else
{
//叔叔不存在,或者为黑
if (cur == parent->_right)
{
//左旋
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//右左旋
RotateR(parent);
RotateL(grandfather);
cur->_col=BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
5.红黑树的验证
如何判断红黑树是否平衡呢?
三个方面:根节点是否为黑色,每条路径的黑色节点数量是否相同 是否存在连续的红色节点
bool IsBalance()
{
if (_root->_col == RED)
{
return false;
}
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return check(_root,0,refNum);
}
bool check(Node* root,int blackNum,const int refNum)
{
if (root == nullptr)
{
if (blackNum != refNum)
{
cout << "存在黑色节点的数量不相等的路径" << "\n";
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红色节点" << "\n";
return false;
}
if (root->_col == BLACK)
{
++blackNum;
}
return check(root->_left, blackNum, refNum) && check(root->_right, blackNum, refNum);
}
6.红黑树和AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,时间复杂度都是O(logN),红黑树不追求绝对平衡,只需保持最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能更好,所以实际运用中红黑树用的更多。