1. 红黑树的概念:
红黑树是一种二叉搜索树,但每个结点上增加一个存储位表示结点颜色(red/back),通过下面的红黑树性质和红黑树确保没有一条路径会比其他路径长出两倍。
2.对比AVL树和红黑树
3.红黑树的性质
1.每个结点只有红黑两种颜色;
2…红黑树的根是黑色;
3.不能出现连续的红色结点;
4.每条路金包含相同数量的黑色结点;
以上4条性质可以保证红黑树的最长路径的节点数不会超过最短路径的节点数的二倍;
问题1
这里如何用性质来保证最长路径的节点数不会超过最短路径的节点数的二倍
路径
这里的路径和我们一般认识的路径不同;
4.红黑树结点的定义:
使用类来定义结点:并使用枚举定义一个颜色变量来存储红黑;
5.红黑树的插入:
红黑树的插入分两步:
1.找到cur要插入的位置;
2.对插入的结点的支路的红黑树进行判断;是否需要调整,如需要进行调整;
步骤1
代码:
步骤二:
步骤二有多种情况:
情况一:新节点的颜色默认是红色;如果双亲结点是黑色——————不需要调整;
情况二:新节点cur是红色;双情接结点p的颜色是红色;祖父节点g是黑色;叔叔结点u存在并为红色;
解决方式:p,u改为黑色;g改为红色;然后把g当成cur,继续向上调整;
情况三 :(单旋转)新节点的颜色是红色,父节点的颜色是红色,叔叔结点存在为黑/叔叔结点不存在;
(1)叔叔不存在
(2)叔叔存在且为黑
解决方式:旋转+变色;
(1)
p为g的左孩子,cur为p的zuo孩子,则进行右单旋;
相反
p为g的右孩子,cur为p的右孩子,则进行左单旋;
(2)
p,g变色-p变黑,g变红;
情况四:(双旋)cur红,p为红,g为黑,u不存在/u存在且为黑;
解决方式:
先对parent进行单旋,将gpc形成单旋树;最后按第3种方法进行单旋转;
插入代码如下:
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;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent==grandfather->_left)
{
// g
// p u
//c
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
//单旋
// g
// p u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//双旋
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
// g
// u p
// c
Node* uncle = grandfather->_left;
if (uncle&&uncle->_col==RED)
{
grandfather->_col = RED;
uncle->_col = parent->_col = BLACK;
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;
}
//左单旋转
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (subRL)
{
subRL->_parent = parent;
}
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
6.红黑树的验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
性质如下:
1…红黑树的根是黑色;
2.不能出现连续的红色结点;
3.每条路金包含相同数量的黑色结点
代码如下
bool check(Node* root, int blacknum, const int refVal)
{
//使用递归来判断是否存在连续红节点;和每条支路的黑色结点数目相同
if (root == nullptr)//到达尾结点
{
if (blacknum != refVal)
{
cout << "存在黑结点数目不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "有连续的红色结点" << endl;
return false;
}
if (root->_col == BLACK)
{
++blacknum;
}
return check(root->_left, blacknum, refVal)
&& check(root->_right, blacknum, refVal);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
bool IsBalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == RED)
{
return false;
}
int refval = 0;//参考值
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refval;
}
cur = cur->_left;
}//将一条支路作为参考的黑结点个数的参考值;
int blacknum = 0;
return check(_root, blacknum, refval);
}
7.红黑树的删除:
红黑树的删除本节不做讲解,有兴趣的同学可参考:《算法导论》或者《STL源码剖析》
红黑树的删除阿里讲解
8.红黑树的应用
- C++ STL库 – map/set、mutil_map/mutil_set
- Java 库
- linux内核
- 其他一些库