虽然写这个博客主要目的是为了给我自己做一个思路记忆录,但是如果你恰好点了进来,那么先对你说一声欢迎。我并不是什么大触,只是一个菜菜的学生,如果您发现了什么错误或者您对于某些地方有更好的意见,非常欢迎您的斧正!
红黑树是许多“平衡”搜索树中的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为O(lgn)。13.1红黑树的性质
红黑树是一棵二叉搜索树,它在每个结点上增加了一份存储位来计表示结点的颜色,可以是Red或Black。树的每个结点包含5个属性:color、key、left、right、parent。
一棵有n个内部结点的红诶书的高度最多为2lg(n+1)。
13.2旋转
指针结构的修改是通过旋转来完成的,这是一种能保持二叉搜索树性质的搜索树局部操作。图中给出了两种旋转:左旋和右旋。
我们来看一下左旋的伪代码:
以下是左旋的图例:
13.3插入
1、黑父
新点的父结点为黑色,插入一个红色结点将不会影响平衡。2、红父
新点的父结点为黑色,插入一个红色结点将不会影响平衡。
蓝色表示未知颜色。以下图片中的箭头表示我们的判定点。
2.1红叔
如图变色,但是需要对红祖进行新一轮的平衡判定。(不论新是左孩子还是右孩子都一样)2.2黑叔
父亲是左孩子,新节点是左孩子。祖结点右旋。(图在下方)
父亲是左孩子,新节点是右孩子。第二个步骤其实就是情况一。(图在下方)
情况三:父亲是右孩子,新节点是右孩子。(图在下方)
父亲是右孩子,新节点是左孩子。(图在下方)
13.4删除
虽然书中说删除比二叉搜索树要复杂很多,但是我们还是先按照一棵二叉搜索树来删除结点,被删除的点的情况主要还是以下三种: ①没有孩子:直接删除
②有一个孩子:用这个孩子顶替它
③有两个孩子:寻找它的后继,用它的后继(寻找一个结点的后继,具体可以看我《算法导论》——第十二章(复制链接自行打开:https://blog.csdn.net/weixin_40851250/article/details/83243817))顶替它,同时按照①或②删除这个后继(后继不会有两个孩子!)
那么它与二叉树的删除的区别在哪里呢?在删除某个结点之后,有很大的可能会使它违背红黑树的性质,所以我们需要通过“旋转和重新着色”来修正这棵树,维护它的性质。那么怎么维护呢?我们一个个分析。
|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·情况①没有孩子:直接删除·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|
显然以下图片所示的情况都不可能发生。
那么就只有这两种情况:|·|·|·|·|·|·|·|·|·|·|·|·|·|·红色叶子结点·|·|·|·|·|·|·|·|·|·|·|·|·|·|
显然直接删除红色的结点就好了。
|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·情况②有一个孩子:用这个孩子顶替它·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|·|
显然,不存在红色结点只有一个孩子的情况。那么黑色结点仅有一个孩子的情况如下:情况2.1待删除的黑色结点仅有一个红孩子
如图(紫色表示颜色不明)。这种情况只要用红色的孩子代替删除的结点并将颜色变换为黑色即可。情况2.2待删除的结点的兄弟是红色
将父结点和兄弟结点的颜色互换,然后将父结点进行左旋或者右旋,就变成了情况2.3情况2.3待删除的结点的兄弟是黑色
2.3.1黑兄红父二黑侄(要删除的结点有两个孩子!后继是删除的右孩子中提上来的!)
将父结点变为黑色,兄结点变为黑色,后继结点变为黑色即可,删除完成。
2.3.2黑兄黑父二黑侄(要删除的结点有两个孩子!)
用后继替换要删除的黑色结点,将父结点变红色, 作为新的判定点向上判断。
2.3.3黑兄远红侄(要删除的结点可能有孩子!)
2.3.4黑兄近红侄(要删除的结点有两个孩子!)
2.3.5红父黑兄无侄(要删除的结点没有孩子!)
2.3.6黑父黑兄无侄(要删除的结点没有孩子!)
好的,不知道看了这么多你有没有晕,反正我是很晕的,那我们就对以上情况做一个总结吧!
(“判断类型的时候,先看待删除的节点的颜色,再看兄弟节点的颜色,再看侄子节点的颜色(侄子节点先看远侄子再看近侄子),最后看父亲节点的颜色。”(这句话摘自青儿哥哥的博客https://www.cnblogs.com/qingergege/p/7351659.html)
这一章我看了很久,真的很头大,还画了这么多鬼图,真心很累的,突然想要求赞,嘤嘤嘤!虽然我写的可能还是很烂吧,比不过那些大牛,emmm当然是比不了的!好的我要放一张图,安慰一下自己,也安慰一下正在被红黑树折磨的你!(图当然是我盗的,我怎么可能养得起猫!还是这么可爱的猫!废话不说我要放代码了!(建议自己复制后放到编辑器中运行一下))
红黑树.h
#pragma once
#include <stdio.h>
/*红黑树的颜色:枚举*/
enum RBtreeNodeColor
{
RED,
BLACK
};
/*红黑树结点*/
typedef struct RBtreeNode
{
int key;/*该结点的数据*/
RBtreeNode *left;/*指向左边结点的指针*/
RBtreeNode *right;/*指向右边结点的指针*/
RBtreeNode *parent;/*指向父结点的指针*/
RBtreeNodeColor color;/*该结点的颜色*/
}RBtreeNode;
/*先序遍历红黑树*/
void PreRBtree(RBtreeNode* root);
/*左旋*/
void LeftRotate(RBtreeNode* &root, RBtreeNode* x);
/*右旋*/
void RightRotate(RBtreeNode* &root, RBtreeNode* y);
/*插入*/
void InsertRBtree(RBtreeNode* &root, RBtreeNode* z);
/*插入修正*/
void InsertFixRBtree(RBtreeNode* &root, RBtreeNode* z);
/*删除*/
void DeleteRBtree(RBtreeNode* &root, RBtreeNode* z);
/*删除调整*/
void DeleteFixRBtree(RBtreeNode* &root, RBtreeNode* child, RBtreeNode* parent);
/*测试函数*/
void TestRBtree();
红黑树.cpp
#include "红黑树.h"
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
/*先序遍历红黑树*/
void PreRBtree(RBtreeNode* root)
{
if (root != NULL)
{
cout << root->key;
if (root->color == RED)cout << "(红)" << "\t";
else cout << "(黑)" << "\t";
PreRBtree(root->left);
PreRBtree(root->right);
}
}
/*左旋*/
void LeftRotate(RBtreeNode* &root, RBtreeNode* x)
{
/*
X Y
/ \ / \
α Y ---> X γ
/ \ / \
β γ α β
*/
RBtreeNode* y = x->right;
x->right = y->left; /*移动β*/
if (y->left != NULL) /*Y.left非空*/
{
y->left->parent = x; /*β.parent=X*/
}
y->parent = x->parent; /*X的父结点成为Y的父结点*/
if (x->parent == NULL) /*X是根结点*/
root = y;
else if (x == x->parent->left) /*X是左儿子*/
x->parent->left = y;
else
x->parent->right = y;
y->left = x;
x->parent = y;
}
/*右旋*/
void RightRotate(RBtreeNode* &root, RBtreeNode* y)
{
/*
Y X
/ \ / \
X γ ---> α Y
/ \ / \
α β β γ
*/
RBtreeNode* x = y->left;
y->left = x->right; /*移动β*/
if (x->right != NULL) /*X.right非空*/
{
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == NULL) /*Y是根结点*/
root = x;
else if (y == y->parent->left) /*Y是左儿子*/
y->parent->left = x;
else
y->parent->right = x;
x->right = y;
y->parent = x;
}
/*插入*/
void InsertRBtree(RBtreeNode* &root, RBtreeNode* z)
{
RBtreeNode* y = NULL;
RBtreeNode* x = root;
while (x != NULL) /*根结点不为空*/
{
y = x; /*y是x的父结点*/
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
if (y == NULL) /*树为空*/
root = z;
else if (z->key < y->key)
y->left = z;
else
y->right = z;
z->left = NULL;
z->right = NULL;
z->color = RED; /*初始置为红色*/
InsertFixRBtree(root, z);
}
/*插入修正*/
void InsertFixRBtree(RBtreeNode* &root, RBtreeNode* z)
{
RBtreeNode* parent = z->parent; /*父结点*/
RBtreeNode* gparent; /*祖父结点*/
while(parent != NULL && parent->color == RED) /*父结点存在且其为红色*/
{
gparent = parent->parent;
if (parent == gparent->left) /*父结点是左孩子*/
{
{
RBtreeNode* uncle = gparent->right; /*情况2.1,叔叔是红色的*/
if (uncle&&uncle->color == RED)
{
uncle->color = BLACK; /*变色*/
parent->color = BLACK;
gparent->color = RED;
z = gparent; /*对祖父继续判断,看是否符合红黑树性质*/
continue;
}
}
if (parent->right == z) /*叔叔是黑色,当前结点是右孩子*/
{
RBtreeNode* tmp;
LeftRotate(root, parent);
tmp = parent;
parent = z;
z = tmp;
}
parent->color = BLACK;
gparent->color = RED;
RightRotate(root, gparent);
}
else /*父结点是右孩子*/
{
{
RBtreeNode* uncle = gparent->left; /*情况2.1,叔叔是红色的*/
if (uncle&&uncle->color == RED)
{
uncle->color = BLACK; /*变色*/
parent->color = BLACK;
gparent->color = RED;
z = gparent; /*对祖父继续判断,看是否符合红黑树性质*/
continue;
}
}
if (z == parent->left) /*黑叔下情况四,父亲是右孩子,新节点是左孩子。*/
{
RightRotate(root, parent); /*父右旋,祖左旋在if外面*/
RBtreeNode* tmp;
tmp = parent; /*parent指向新插入的结点*/
parent = z;
z = tmp;
}
parent->color = BLACK; /*黑叔下情况三:情况三:父亲是右孩子,新节点是右孩子。*/
gparent->color = RED;
LeftRotate(root, gparent); /*祖左旋*/
}
}
root->color = BLACK; /*将根结点设为黑色*/
}
/*删除*/
void DeleteRBtree(RBtreeNode* &root, RBtreeNode* z)
{
RBtreeNode* child;
RBtreeNode* parent;
RBtreeNodeColor color;
if (z->left != NULL && z->right != NULL) /*有两个孩子*/
{
RBtreeNode* replace = z; /*获取后继结点*/
replace = replace->right;
while (replace->left != NULL)
replace = replace->left;
if (z->parent != NULL) /*不是根结点*/
{
if (z->parent->left == z) /*待删除的点是左儿子*/
z->parent->left = replace;
else
z->parent->right = replace;
}
else /*待删除的点是根结点*/
root = replace; /*更新根结点*/
child = replace->right; /*后继结点的右孩子(它没有左孩子!)*/
parent = replace->parent; /*后继结点的父亲*/
color = replace->color; /*后继结点的颜色*/
if (parent == z) /*后继结点是被删除节点的孩子*/
{
parent = replace; /*父结点变成了后继结点*/
}
else /*后继结点不是被删除节点的孩子*/
{
if (child) /*如果后继结点存在孩子*/
child->parent = parent; /*后继结点的父亲变成它的孩子的父亲*/
parent->left = child;
replace->right = z->right;
z->right->parent = replace;
}
replace->parent = z->parent;
replace->color = z->color;
replace->left = z->left;
z->left->parent = replace;
if (color == BLACK) /*如果要删除的是黑色结点*/
DeleteFixRBtree(root, child, parent);
delete z;
return;
}
if (z->left != NULL) /*如果只有左儿子或没有右儿子或没孩子*/
child = z->left; /*保存结点的儿子、父亲和颜色*/
else
child = z->right;
parent = z->parent;
color = z->color;
if (child) /*如果存在孩子*/
child->parent = parent;
if (parent) /*如果不是根结点*/
{
if (parent->left == z) /*z是左儿子*/
parent->left = child;
else
parent->right = child;
}
else
root = child;
if (color == BLACK)
DeleteFixRBtree(root, child, parent);
delete z;
}
/*删除调整*/
void DeleteFixRBtree(RBtreeNode* &root, RBtreeNode* child, RBtreeNode* parent)
{
RBtreeNode* bro;
while ((!child || child->color == BLACK) && child != root)
{
if (parent->left == child)/*左儿子*/
{
bro = parent->right;/*兄弟结点*/
if (bro->color == RED)/*兄弟是红色的(情况2.2)*/
{
bro->color = BLACK;
parent->color = RED;
LeftRotate(root, parent);
bro = parent->right;
}
if ((!bro->left || bro->left->color == BLACK) && (!bro->right || bro->right->color == BLACK))/*黑兄二黑侄*/
{
bro->color = RED;
child = parent;
parent = child->parent;
}
else
{
if (!bro->right || bro->right->color == BLACK)/*有个红孩子,情况2.3.3*/
{
bro->left->color = BLACK;
bro->color = RED;
RightRotate(root, parent);
bro = parent->right;
}
/*情况2.3.4*/
bro->color = parent->color;
parent->color = BLACK;
bro->right->color = BLACK;
LeftRotate(root, parent);
child = root;
break;
}
}
else
{
bro = parent->left;
if (bro->color == RED)
{
bro->color = BLACK;
parent->color = RED;
RightRotate(root, parent);
bro = parent->left;
}
if ((!bro->left || bro->left->color == BLACK) && (!bro->right || bro->right->color == BLACK))
{
bro->color = RED;
child = parent;
parent = child->parent;
}
else
{
if (!bro->left || bro->left->color == BLACK)
{
bro->right->color = BLACK;
bro->color = RED;
LeftRotate(root, bro);
bro = parent->left;
}
bro->color = parent->color;
parent->color = BLACK;
bro->left->color = BLACK;
child = root;
break;
}
}
}
if (child)
child->color = BLACK;
}
/*测试函数*/
void TestRBtree()
{
RBtreeNode* root = (RBtreeNode*)malloc(sizeof(RBtreeNode));/*建立二叉树的根节点*/
srand((unsigned)time(NULL));
int i;
/*初始化根节点*/
root->key = rand() % 200 + 1;/*1-100之间的数*/
root->parent = NULL;
root->left = NULL;
root->right = NULL;
root->color = BLACK;
RBtreeNode *six = (RBtreeNode*)malloc(sizeof(RBtreeNode));/*第六个结点*/
//RBtreeNode *temp = (RBtreeNode*)malloc(sizeof(RBtreeNode));/*临时节点*/
for (i = 0; i < 10; i++)
{
RBtreeNode *data = (RBtreeNode*)malloc(sizeof(RBtreeNode));
data->key = rand() % 200 + 1;/*1-200之间的数*/
data->left = NULL;
data->right = NULL;
data->parent = NULL;
data->color = RED;
if (i == 6)six = data;
InsertRBtree(root, data);
}
cout << "插入10个数字后:" << endl;
cout << "先序遍历:" << endl;
PreRBtree(root);
cout << endl;
cout << endl;
cout << "删除第六个结点(" <<six->key<<")"<< endl;
DeleteRBtree(root, six);
PreRBtree(root);
cout << endl;
}
主函数
#include "红黑树.h"
#include <stdio.h>
int main()
{
TestRBtree();
getchar();
getchar();
return 0;
}
运行结果
我们用给定的数字做一下测试(我写的代码是随机给数字的):我们将会删除78这个结点:我们可以看到90就上位了变黑了。
如果我们删除35,它的后继40就会代替它
如果要删除结点27,对应情况2.3.3
我不知道怎样才算是转载什么的,有些内容确实是集百家之长糅合在一起的,所以如果有冒犯到这些大佬的地方,还望指正。
参考网站以及博客(打开不了就复制链接):
CloudDeveloper的博客 (如果链接为404就复制以下链接自行打开)
https://www.cnblogs.com/bakari/p/4900895.html
skywang12345的博客(如果链接为404就复制以下链接自行打开)
https://www.cnblogs.com/skywang12345/p/3245399.html#a3
极易理解的红黑树讲解(这篇文章讲的非常好!)
https://blog.csdn.net/lc0817/article/details/48345999
数据结构之红黑树
http://dongxicheng.org/structure/red-black-tree/
青儿哥哥的博客
https://www.cnblogs.com/qingergege/p/7351659.html