定义
红黑树是一种二叉搜索树
每个结点上增加一个存储位表示结点的颜色,可以是Red或Black 通过对任何一条从根到叶子的路径上各个结点着色方式的限制。
红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树是如何保证该核心属性的呢?
通过下列五个性质
性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点,即NIL节点)
通过上述性质,红黑树很好的实现了没有一条路径比其他路径长两倍
没有一条路径比其他路径长两倍也可以为以下含义
红黑树中最长路径的长度不能超过最短路径的两倍
性质1,2,5是红黑树的基本性质,需要满足。没什么要讨论的。
性质3与4是解决长度问题的关键
性质3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
也可以理解为 不能出现两个连续的红节点
性质4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
性质3与4结合来看
首先每条路径上黑节点的数目要一致,红色节点不能连着出现
当一条路径最短时,节点均为黑色;
当一条路径最长时,每个黑节点下均链接着红色节点
MAX = 2*MIN;
也就是满足了
红黑树中最长路径的长度不能超过最短路径的两倍
插入
红黑树的插入大体部分与AVL树相近,但调整时有所不同
插入的返回值时一个键值对
当插入成功时 iterator 指向插入的节点 , bool值为true
拆入失败时,即该树中已经存在该元素,iterator为原有节点,bool值为false
std::pair<iterator,bool> Insert(const ValueType& data)
{
//判断是否为空树,为空树创建第一个节点
if (_pHead == nullptr)
{
_pHead = new Node(data, BLACK);
return std::make_pair(iterator(_pHead),true);
}
//按照二叉搜索树的规则进行插入
//从根节点开始查找位置,根节点的父节点为nullptr
pNode parent = nullptr;
pNode cur = _pHead;
//当cur指向空时,当前位置即是目标位置
while (cur)
{
//parent在查询元素目标位置的过程中,
//一定会指向cur的位置
parent = cur;
//二叉树具有排异性,当树中存在相同元素时,不能再插入
if (KeyOfValue(cur->_data) == KeyOfValue(data))
{
return pair(cur, false);
}
else if (KeyOfValue(cur->_data) < KeyOfValue(data))
{
cur = cur->_pRight;
}
else
{
cur = cur->_pLeft;
}
}
//进行元素的插入
cur = new Node(data);
cur->_pParent = parent;
if (KeyOfValue(cur->_data) > KeyOfValue(parent->_data))
{
parent->_pRight = cur;
}
else
{
parent->_pLeft = cur;
}
pNode cur_copy = cur;
//红黑树的调整
//uncle
// uncle = cur == parent->_pLeft ? parent->_pRight : parent->_pLeft;
while (parent && parent->_pParent && parent->_color == RED)
{
pNode grand = parent->_pParent;
//parent在左侧
if (parent == grand->_pLeft)
{
pNode uncle = grand->_pRight;
//情况二与三
if (uncle == nullptr || uncle->_color == BLACK)
{
//异侧情况三
if (cur == parent->_pRight)
{
RotateL(parent);
std::swap(parent, cur);
}
//情况二
RotateR(grand);
parent->_color = BLACK;
grand->_color = RED;
if (cur == _pHead)
cur->_color = BLACK;
break;
}
//情况一
else
{
uncle->_color = parent->_color = BLACK;
//grand为根时 直接置为黑
if (grand == _pHead)
{
grand->_color = BLACK;
break;
}
else
{
grand->_color = RED;
cur = grand;
parent = cur->_pParent;
}
}
}
//parent在右侧
else
{
pNode uncle = grand->_pLeft;
//情况二与三
if (uncle == nullptr || uncle->_color == BLACK)
{
//异侧情况三
if (cur == parent->_pLeft)
{
RotateR(parent);
std::swap(parent, cur);
}
//情况二
RotateL(grand);
parent->_color = BLACK;
grand->_color = RED;
if (cur == _pHead)
cur->_color = BLACK;
break;
}
//情况一
else
{
uncle->_color = parent->_color = BLACK;
//grand为根时 直接置为黑
if (grand == _pHead)
{
grand->_color = BLACK;
break;
}
else
{
grand->_color = RED;
cur = grand;
parent = cur->_pParent;
}
}
}
}
return std::make_pair(iterator(cur_copy), true);
}
调整
调整红黑树的前提是,插入破坏了红黑树的结构
新节点的默认颜色是红色,只有当双亲节点为红色时才破坏了红黑树的结构
当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
红黑树的调整可以分为3中情况
以单侧举例
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
情况一: cur为红,p为红,g为黑,u存在且为红
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
说明: u的情况有两种
1.如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点则cur和p一定有一个节点的颜色是黑色,就不满足性质4: 每条路径黑色节点个数相同。
2.如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。
解决方案
p为g的左孩子,cur为p的左孩子,则进行右单旋转
p、g变色–p变黑,g变红
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转。后转为情况二