红黑树的性质
保证树中最长路径的长度不超过最短路径的长度的两倍
用什么方法保证上面这一点?将树中的结点视为是有颜色的
采用如下的规则:
rule1: 树中的结点不是红色就是黑色
rule2: 树的根节点是黑色的
rule3: 如果一个结点是红色的,则这个结点的左右孩子都是黑色的
rule4: 对于每个结点,该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
rule5: 每个叶 子节点都是黑色的
对rule5的说明
rule5保证了规则体系完备,空结点视为是叶子节点,那么每个空结点就决定了一条路径,
rule4在计算路径长度的时候,并不把空结点计算在内,但是只要存在路径就必须计算长度
比如 下面的左下图是红黑树,右下图不是红黑树,(-代表当前结点和有非空左孩子或者右孩子)
-B- -B-
B -R- B R-
B -B -B
R
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
//紧接着的if()else{}判断叔叔所在的位置
// 这个位置直接确定了下面旋转的(如果需要的话)方向
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
{
// 当叔叔的颜色为黑,那么要考虑新插入的结点是在
// 其父亲结点 p 的左子树上,还是在其父亲结点的右子树上,
// 以确定旋转是进行两次,还是进行一次
if (cur == parent->_left)
{
//如果是如图所示的结构,只需要调整 c p g 条路径
// 调整的方式是右单旋,函数传入的参数是g
// g
// p u
// c
Rotateright(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//如果是如图所示的结构,只需要调整 c p g 条路径(其中c是p的右子树)
// 调整的方式是先左单旋,函数传入的参数是p,调整p和c的父子关系
// 再右单旋,传入的参数是g
// g
// p u
// c
Rotateleft(parent);
Rotateright(grandfather);
//此处一定要画图进行理解,cur经过两次最后成为了辈分最高的结点
//p和g成为了兄弟,而且都需要变成红色
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)
{
// g
// u p
// c
Rotateleft(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
Rotateright(parent);
Rotateleft(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
R