
红黑树:
红黑树和AVL树的对比:
C++的STL的map和set用红黑树,因为要增,删,查,这些操作都比较多。

红黑树也是一棵二叉搜索树。 满足每个节点的左子树的值都小于节点的值,节点的值都小于节点的右子树的值。(上面画的10和30不准确)
红黑树的5个性质:
我们在进行插入和删除中,以上性质必须维持着!!!
面试问题:在红黑树中,节点的左右子树的高度差最多不能超过多少?
根节点root分别到叶子节点1, 2,假设下面这种极端的情况:

所以在红黑树中,长的最多不能超过短的2倍。
我们还是按照BST树的插入方式进行的:只是增加了条件(需要满足红黑树的5个性质):
如果父亲是红色节点,造成连续的红色节点,我们进行插入的调整如下:
叶子节点的局部树 的图如下:

我们要新插入节点C,红色,但是父节点B也是红色,所以现在不满足红黑树的性质。
要进行调整:


这样,又产生连续红色的节点了!!!我们在插入的前提还要看看叔叔!!!
有叔叔的情况1:

我们插入了一个C节点,连续的红色节点出现了,现在打算把父亲B的颜色和爷爷A的颜色交换,但是如果爷爷A是红色,和叔叔D就是连续红色了。
我们在插入C,发现其父亲B和叔叔D都是红色,我们把爷爷A的颜色改为红色,父亲B的颜色改为黑色,把叔叔D的颜色改为黑色。

上图不是完全的红黑树,只是局部的,从局部来看,已经没有出现连续的红色节点,A往左右走也都是1个黑色节点。
但是爷爷A的父亲有可能是黑色,也有可能是红色哦!
x是新插入的节点C
调整后,我们让x指向A,
然后继续向上回溯调整,和刚才一样的方式!

但是,根节点的颜色必须强制为黑色!!!
插入节点C的叔叔节点是黑色的。
此时就不能把父亲节点的颜色和爷爷节点的颜色交换了。

实际上,上图的操作是不行的。
从上往下,沿着根节点的某一个路径,原来经过A到左边1个黑色,经过A右边2个黑色,经过交换颜色后,现在经过A到右边就只有1个黑色了!!!
黑色节点的数量就不符合性质了。
我们应该:
原来黑色的A是左右两边的公共节点,它对左右两个局部路径都增加1个黑色的数量,现在把A涂成红色,很明显,往右走,就少了1个黑色节点了。
但是我们再多做1步操作就可以了:
C的父亲与爷爷交换颜色后,以A为轴,右旋转一下

这样就OK了!!!

插入的C节点没有和父亲B,爷爷A在同一个方向上。

如果把C的父亲B的颜色和爷爷A的颜色一交换,然后A为轴右旋转一下,行不行?
不行!

A和C还是连续的红色了。
这样解决不了问题!
我们得学学情况2的思想:

在红黑树中,访问节点的时候,要访问到它的父亲,爷爷和叔叔。
包括删除,还要访问它的兄弟节点。
在递归的过程中就不方便了,而且对于红黑树来说,插入操作最多旋转2次,局部解决完之后,局部没有改变红黑树的性质,全局自然就维护了红黑树的性质,局部解决好了,就不用向上回溯了,所以我们不需要使用递归,递归的话要从插入的地方回溯到根节点,浪费效率。
红黑树中只有左旋和右旋,没有左平衡和右平衡!

//左旋转
void leftRotate(Node* node)
{
Node* child = node->right_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node本身就是root节点
root_ = child;//child变成根节点了
}
else//node的父节点不为空
{
if (node->parent_->left_ == node)//node在父节点的左孩子
{
node->parent_->left_ = child;
}
else//node在父节点的右孩子
{
node->parent_->right_ = child;
}
}
node->right_ = child->left_;
if (child->left_ != nullptr)
{
child->left_->parent_ = node;
}
child->left_ = node;
node->parent_ = child;
}

//右旋转
void rightRotate(Node* node)
{
Node* child = node->left_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node原来就是root节点
root_ = child;
}
else
{
if (node->parent_->left_ == node)
{
//node在父节点的左边
node->parent_->left_ = child;
}
else
{
//node在父节点的右边
node->parent_->right_ = child;
}
}
node->left_ = child->right_;
if (child->right_ != nullptr)
{
child->right_->parent_ = node;
}
child->right_ = node;
node->parent_ = child;
}
//插入操作
void insert(const T& val)
{
if (root_ == nullptr)
{
root_ = new Node(val);//刚好默认是黑色
return;
}
Node* parent = nullptr;
Node* cur = root_;
while (cur != nullptr)
{
if (cur->data_ > val)//当前节点的值大于要插入的值
{
parent = cur;
cur = cur->left_;
}
else if (cur->data_ < val)//当前节点的值小于要插入的值
{
parent = cur;
cur = cur->right_;
}
else//值存在,不插入重复的值
{
return;
}
}
//设置当前节点的parent和颜色
Node* node = new Node(val, parent, nullptr, nullptr, RED);//新节点设置成红色
if (parent->data_ > val)
{
parent->left_ = node;//插在父亲的左孩子域
}
else
{
parent->right_ = node;//插在父亲的右孩子域
}
//如果新插入的红色节点,父节点也是红色,不满足红黑树性质,进行插入调整操作
if (RED == color(parent))
{
fixAfterInsert(node);
}
}
//红黑树的插入调整操作
void fixAfterInsert(Node* node)
{
//如果当前红色节点的父节点也是红色,继续调整
while (color(parent(node)) == RED)
{
if (left(parent(parent(node))) == parent(node))//爷爷节点的左孩子是我的父亲
{
//表示插入的节点在左子树当中,叔叔在右边,插入要看叔叔
Node* uncle = right(parent(parent(node)));//叔叔在爷爷的右孩子
if (RED == color(uncle))//情况一,叔叔节点也是红色
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node));//node指向他的爷爷,继续调整上去
}
else//叔叔节点是黑色
{
//先处理情况三,插入的节点和父亲,爷爷不在一侧
if (right(parent(node)) == node)
{
node = parent(node);
leftRotate(node);
//进行左旋转,node此时指向的就是情况二中的左边最后一个节点的位置
}
//统一处理情况二
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
rightRotate(parent(parent(node)));//右旋转
break;//调整已经完成
}
}
else//插入的节点在右子树当中
{
Node* uncle = left(parent(parent(node)));
if (RED == color(uncle))//情况一
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node));//继续调整
}
else
{
//先处理情况三
if (left(parent(node)) == node)
{
node = parent(node);
rightRotate(node);
}
//统一处理情况二
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
leftRotate(parent(parent(node)));
break;//调整已经完成
}
}
}
//此处强制root为黑色节点
setColor(root_, BLACK);
}
1、发现23是连续的红色节点;(插入的节点就是红色的)

这属于我们情况2的镜像情况:(这里的叔叔其实没有,也是黑色)

将插入节点的父节点变为黑色,将爷爷节点变为红色,并且以爷爷节点做左旋操作!

2、再插入4,34都是红色,不满足性质,看叔叔,红色;


此时2变红了,还需要以2继续向上调整。2的父亲是空,此时我们跳出循环,将2置为黑色,因为它是根节点!

3、继续插入5,45不满足性质

看叔叔,叔叔没有,在3的左侧,没有就是黑色,黑色就是情况2;

将5的父节点4变为黑色,爷爷3变为红色,以5的爷爷节点4作为根节点进行左旋转(只对345进行左旋转);

4、插入6,56都是红色节点,不满足性质

对应情况1;
将父亲和叔叔3和5变为黑色,将爷爷4变为红色;
再到4进行相同操作,4的父节点为黑色,满足性质,退出!

5、插入7,67不满足性质;
5的左边为叔叔节点,为黑色,

对应情况2:

将父节点6变为黑色,将爷爷节点5变为红色,以5进行左旋:

我们也可以发现红黑树不是一个平衡树!
6、插入8,78红色,不满足红黑树的性质;

看叔叔5,红色,满足情况1;

将父节点5和叔叔节点7变为黑色,将爷爷6变为红色;

此时发现4和6都是红色,不满足性质,将节点指向爷爷6,继续向上调整;

此时叔叔节点为1,黑色,且4和6在同一侧,对应情况2;
将父节点4和爷爷节点2将颜色换下,在以爷爷2节点进行左旋转:

7、插入9,89不满足性质;
9的叔叔节点没有,默认是黑色;

满足情况2;

将9的父节点变为8变为黑色,将爷爷节点7变为红色,以爷爷节点7进行左旋转;

8、插入10,910不满足二叉树的性质;

对应情况1;
将父节点和叔叔节点变为黑色,将爷爷节点变为红色;

此时68变为红色,不满足性质,将节点指向爷爷节点8,继续调整:
此时叔叔2为红色,对应情况1,将叔叔2和父亲6都置为黑色,再将爷爷节点4置为红色;

再将节点指向爷爷节点4,继续向上操作,发现4的父节点为空,4是根节点,跳出循环,将根节点4置为黑色!
最终结果:

int main()
{
RBTree<int> rb;
for (int i = 1; i <= 4; ++i)
{
rb.insert(i);
}
return 0;
}
删除一个节点:


1、如果删除的是1个红色节点(父亲和孩子肯定是黑色),不影响该路径上黑色节点的数量,都不会破坏红黑树的性质,所以不做任何删除的调整工作。
2、如果删除的是1个黑色节点,孩子要补上去,占据它原来的位置;
分2种情况:
如果补上来的孩子是红色节点,直接把这个孩子涂成黑色, 调整完成!!!
如果补上来的孩子是黑色节点,那在当前的路径上没办法再补一个黑色节点出来了。只能从兄弟那里借调黑色节点了。
分成4种情况:
我们画的是局部的。
情况1:

我们现在要删除B节点,B以及B的孩子是黑色的。
删除B后,要从兄弟借黑色节点,首先兄弟本身就得是黑色。其次,兄弟的孩子也得是红色。我们把兄弟的孩子的红色补成黑色。我们把C的黑色拿到左边来。
父节点A有可能是红也有可能是黑。我们这么调整:


情况2:
我们要删除B节点,但是兄弟节点是黑色的,但是红色节点不在兄弟的右孩子,在兄弟的左孩子。

如果我们以A节点进行左旋转的话:
旋转的时候,我们又不能让D跑到左子树这边上去了。我们要保证的是兄弟的右孩子是红色的,然后才能补上黑色,补齐黑色的个数。
所以我们要处理的是:



如果兄弟的2个节都是红色,也是归结于情况1,只需要保证兄弟的右孩子是红色,就不用关心兄弟的左孩子了
情况3:

我们要删除B,但是B的兄弟C节点及其孩子都是黑色的。因为我们的黑色数量是不能变的。兄弟借不了你了。
如果兄弟借给你黑色,那么他就没有红色来补黑色了。
最差的情况是:只能向上一直回溯。让所有路径都少一个黑色出来。
我们来看看:

情况4:
我们要删除B节点,但是兄弟是红色。

首先我们进行左旋转操作,C和A的颜色对调。然后以A为轴左旋转。


情况4处理之后,保证了现在待删除节点B的兄弟节点是黑的!
然后现在我们要删除B节点,此时又归结到上面的3种情况了:兄弟是黑色的!
可以看出红黑树的删除操作,最多旋转3次!(插入最多旋转2次)
//红黑树删除操作
void remove(const T& val)
{
if (root_ == nullptr)
{
return;
}
Node* cur = root_;//指向根节点
while (cur != nullptr)
{
if (cur->data_ > val)//当前节点值大于要删除的值
{
cur = cur->left_;
}
else if (cur->data_ < val)//当前节点值小于要删除的值
{
cur = cur->right_;
}
else//找到了,跳出循环
{
break;
}
}
//如果是空,则没找到val节点,返回
if (cur == nullptr)
{
return;
}
//删除前驱节点 情况三 有2个孩子
if (cur->left_ != nullptr && cur->right_ != nullptr)
{
Node* pre = cur->left_;//定义前驱节点
while (pre->right_ != nullptr)
{
pre = pre->right_;
}
cur->data_ = pre->data_;
cur = pre;//cur指向前驱节点
}
//删除cur指向的节点 情况一和二
Node* child = cur->left_;//让child指向不为空的孩子
if (child == nullptr)
{
child = cur->right_;
}
//删除后,不用考虑平衡!
//我们节点有left right parent 3个域
if (child != nullptr)//child不为空
{
child->parent_ = cur->parent_;//cur是待删节点
if (cur->parent_ == nullptr)
{
//root_ -> cur_
root_ = child;
}
else
{
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = child;
}
else
{
cur->parent_->right_ = child;
}
}
Color c = color(cur);//记录删除节点的颜色
delete cur;
if (c == BLACK)//删除的是黑色节点,要进行删除调整操作
{
fixAfterRemove(child);
}
}
else//child为空
{
//child == nullptr
if (cur->parent_ == nullptr)
{
delete cur;
root_ = nullptr;
return;
}
else
{
//删除的cur就是叶子节点
if (color(cur) == BLACK)
{
fixAfterRemove(cur);
}
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = nullptr;
}
else
{
cur->parent_->right_ = nullptr;
}
delete cur;
}
}
}
//红黑树的删除调整操作
void fixAfterRemove(Node* node)
{
while (node != root_ && color(node) == BLACK)
{
if (left(parent(node)) == node)//删除的黑色节点在左子树
{
Node* brother = right(parent(node));//指向兄弟
if (color(brother) == RED)//情况四 兄弟是红色的
{
setColor(parent(node), RED);
setColor(brother, BLACK);
leftRotate(parent(node));
brother = right(parent(node));
}
if (color(left(brother)) == BLACK
&& color(right(brother)) == BLACK)//情况三 兄弟和孩子是黑的
{
setColor(brother, RED);
node = parent(node);
}
else
{
if (color(right(brother)) != RED)//情况二 兄弟的右孩子不是红色的
{
setColor(brother, RED);
setColor(left(brother), BLACK);
rightRotate(brother);
brother = right(parent(node));
}
//归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(right(brother), BLACK);
leftRotate(parent(node));
break;
}
}
else//删除的黑色节点在右子树
{
Node* brother = left(parent(node));
if (color(brother) == RED) // 情况四
{
setColor(parent(node), RED);
setColor(brother, BLACK);
rightRotate(parent(node));
brother = left(parent(node));
}
if (color(left(brother)) == BLACK
&& color(right(brother)) == BLACK)//情况三
{
setColor(brother, RED);
node = parent(node);
}
else
{
if (color(left(brother)) != RED)//情况二
{
setColor(brother, RED);
setColor(right(brother), BLACK);
leftRotate(brother);
brother = left(parent(node));
}
//归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(left(brother), BLACK);
rightRotate(parent(node));
break;
}
}
}
//如果发现node指向的节点是红色,直接涂成黑色,调整结束
setColor(node, BLACK);
}

如果将9删除:



template<typename T>
class RBTree
{
public:
RBTree() :root_(nullptr) {}
struct Node;
//插入操作
void insert(const T& val)
{
if (root_ == nullptr)
{
root_ = new Node(val);//刚好默认是黑色
return;
}
Node* parent = nullptr;
Node* cur = root_;
while (cur != nullptr)
{
if (cur->data_ > val)//当前节点的值大于要插入的值
{
parent = cur;
cur = cur->left_;
}
else if (cur->data_ < val)//当前节点的值小于要插入的值
{
parent = cur;
cur = cur->right_;
}
else//值存在,不插入重复的值
{
return;
}
}
//设置当前节点的parent和颜色
Node* node = new Node(val, parent, nullptr, nullptr, RED);//新节点设置成红色
if (parent->data_ > val)
{
parent->left_ = node;//插在父亲的左孩子域
}
else
{
parent->right_ = node;//插在父亲的右孩子域
}
//如果新插入的红色节点,父节点也是红色,不满足红黑树性质,进行插入调整操作
if (RED == color(parent))
{
fixAfterInsert(node);
}
}
//红黑树的插入调整操作
void fixAfterInsert(Node* node)
{
//如果当前红色节点的父节点也是红色,继续调整
while (color(parent(node)) == RED)
{
if (left(parent(parent(node))) == parent(node))//爷爷节点的左孩子是我的父亲
{
//表示插入的节点在左子树当中,叔叔在右边,插入要看叔叔
Node* uncle = right(parent(parent(node)));//叔叔在爷爷的右孩子
if (RED == color(uncle))//情况一,叔叔节点也是红色
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node));//node指向他的爷爷,继续调整上去
}
else//叔叔节点是黑色
{
//先处理情况三,插入的节点和父亲,爷爷不在一侧
if (right(parent(node)) == node)
{
node = parent(node);
leftRotate(node);
//进行左旋转,node此时指向的就是情况二中的左边最后一个节点的位置
}
//统一处理情况二
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
rightRotate(parent(parent(node)));//右旋转,以爷爷节点进行右旋
break;//调整已经完成
}
}
else//插入的节点在右子树当中
{
Node* uncle = left(parent(parent(node)));
if (RED == color(uncle))//情况一
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node));//继续调整
}
else
{
//先处理情况三
if (left(parent(node)) == node)
{
node = parent(node);
rightRotate(node);
}
//统一处理情况二
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
leftRotate(parent(parent(node)));
break;//调整已经完成
}
}
}
//此处强制root为黑色节点
setColor(root_, BLACK);
}
//红黑树删除操作
void remove(const T& val)
{
if (root_ == nullptr)
{
return;
}
Node* cur = root_;//指向根节点
while (cur != nullptr)
{
if (cur->data_ > val)//当前节点值大于要删除的值
{
cur = cur->left_;
}
else if (cur->data_ < val)//当前节点值小于要删除的值
{
cur = cur->right_;
}
else//找到了,跳出循环
{
break;
}
}
//如果是空,则没找到val节点,返回
if (cur == nullptr)
{
return;
}
//删除前驱节点 情况三 有2个孩子,这里还没有删除
if (cur->left_ != nullptr && cur->right_ != nullptr)
{
Node* pre = cur->left_;//定义前驱节点
while (pre->right_ != nullptr)
{
pre = pre->right_;
}
cur->data_ = pre->data_;
cur = pre;//cur指向前驱节点
}
//删除cur指向的节点 情况一和二
Node* child = cur->left_;//让child指向不为空的孩子
if (child == nullptr)
{
child = cur->right_;
}
//删除后,不用考虑平衡!
//我们节点有left right parent 3个域
if (child != nullptr)//child不为空
{
child->parent_ = cur->parent_;//cur是待删节点
if (cur->parent_ == nullptr)
{
//root_ -> cur_
root_ = child;
}
else
{
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = child;
}
else
{
cur->parent_->right_ = child;
}
}
Color c = color(cur);//记录删除节点的颜色
delete cur;
if (c == BLACK)//删除的是黑色节点,要进行删除调整操作
{
fixAfterRemove(child);
}
}
else//child为空
{
//child == nullptr
if (cur->parent_ == nullptr)
{
delete cur;
root_ = nullptr;
return;
}
else
{
//删除的cur就是叶子节点
if (color(cur) == BLACK)
{
fixAfterRemove(cur);
}
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = nullptr;
}
else
{
cur->parent_->right_ = nullptr;
}
delete cur;
}
}
}
//红黑树的删除调整操作
void fixAfterRemove(Node* node)
{
while (node != root_ && color(node) == BLACK)
{
if (left(parent(node)) == node)//删除的黑色节点在左子树
{
Node* brother = right(parent(node));//指向兄弟
if (color(brother) == RED)//情况四 兄弟是红色的
{
setColor(parent(node), RED);
setColor(brother, BLACK);
leftRotate(parent(node));
brother = right(parent(node));
}
if (color(left(brother)) == BLACK
&& color(right(brother)) == BLACK)//情况三 兄弟和孩子是黑的
{
setColor(brother, RED);
node = parent(node); //指针指向父亲
}
else
{
if (color(right(brother)) != RED)//情况二 兄弟的右孩子不是红色的
{
setColor(brother, RED);
setColor(left(brother), BLACK);
rightRotate(brother);
brother = right(parent(node));
}
//最后全部归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(right(brother), BLACK);
leftRotate(parent(node));
break;
}
}
else//删除的黑色节点在右子树
{
Node* brother = left(parent(node));
if (color(brother) == RED) // 情况四
{
setColor(parent(node), RED);
setColor(brother, BLACK);
rightRotate(parent(node));
brother = left(parent(node));
}
if (color(left(brother)) == BLACK
&& color(right(brother)) == BLACK)//情况三
{
setColor(brother, RED);
node = parent(node);
}
else
{
if (color(left(brother)) != RED)//情况二
{
setColor(brother, RED);
setColor(right(brother), BLACK);
leftRotate(brother);
brother = left(parent(node));
}
//归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(left(brother), BLACK);
rightRotate(parent(node));
break;
}
}
}
//如果发现node指向的节点是红色,直接涂成黑色,调整结束
setColor(node, BLACK);
}
private:
//红黑树节点的颜色
enum Color
{
BLACK,
RED
};
//节点类型
struct Node
{
Node(T data = T(), Node* parent = nullptr,
Node* left = nullptr, Node* right = nullptr,
Color color = BLACK)
:data_(data)
, left_(left)
, right_(right)
, parent_(parent)
, color_(color)
{}
T data_;
Node* left_;
Node* right_;
Node* parent_;//指向当前节点的父节点
Color color_; //节点的颜色
};
//返回节点的颜色
Color color(Node* node)
{
return node == nullptr ? BLACK : node->color_;
}
//设置节点颜色
void setColor(Node* node, Color color)
{
node->color_ = color;
}
//返回节点的左孩子
Node* left(Node* node)
{
return node->left_;
}
//返回节点的右孩子
Node* right(Node* node)
{
return node->right_;
}
//返回节点的父亲
Node* parent(Node* node)
{
return node->parent_;
}
//左旋转
void leftRotate(Node* node)
{
Node* child = node->right_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node本身就是root节点
root_ = child;//child变成根节点了
}
else//node的父节点不为空
{
if (node->parent_->left_ == node)//node在父节点的左孩子
{
node->parent_->left_ = child;
}
else//node在父节点的右孩子
{
node->parent_->right_ = child;
}
}
node->right_ = child->left_;
if (child->left_ != nullptr)
{
child->left_->parent_ = node;
}
child->left_ = node;
node->parent_ = child;
}
//右旋转
void rightRotate(Node* node)
{
Node* child = node->left_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node原来就是root节点
root_ = child;
}
else
{
if (node->parent_->left_ == node)
{
//node在父节点的左边
node->parent_->left_ = child;
}
else
{
//node在父节点的右边
node->parent_->right_ = child;
}
}
node->left_ = child->right_;
if (child->right_ != nullptr)
{
child->right_->parent_ = node;
}
child->right_ = node;
node->parent_ = child;
}
//指向红黑树的根节点
Node* root_;
};
int main()
{
RBTree<int> rb;
for (int i = 1; i <= 4; ++i)
{
rb.insert(i);
}
rb.remove(9);
rb.remove(10);
rb.remove(5);
rb.remove(3);
return 0;
}