数据结构之红黑树

平衡二叉树(AVL)

左右子树的高度相差不超过 1 的树为平衡二叉树。为了提高二叉搜索树的查找效率而提出的。(二叉搜索树可能会退化成链,此时查找时间复杂度为O(n))
平衡二叉树的优点是搜索效率很快,但是插入和删除效率不高(要严格保证平衡因子不大于1,需要不断消除最小失衡子树),
如果插入和删除频繁的话,平衡二叉树的效率又体现不出来了。
在此基础上,又发展出了红黑树的这种数据结构,红黑树相对平衡二叉树来说,插入和删除效率较高,插入最多只需要2次旋转,删除最多需要3次旋转。

红黑树

红黑树是对树结构的一种高度综合运用,涉及到多叉树,树平衡调整,节点旋转等等,这些是对数据结构基本功的最佳历练。

应用场景

红黑树是一种性能非常稳定的二叉查找树,所以,在工程中,但凡是用到动态插入、删除、查找数据的场景,都可以用到它。
诸如Java中HashMap的底层实现,在JDK1.8中为了解决过度哈希冲突带来的长链表,会将链表转为红黑树;
Linux底层的CFS进程调度算法中,vruntime利用红黑树来进行存储;多路复用技术的Epoll的核心结构也是红黑树+双向链表。
我们不会直接去手写一个可用的红黑树,但是了解红黑树的结构,有助于我们去理解一些底层具体实现。

五大规则

0、节点非黑即红
1、nil节点为黑
2、根节点为黑
3、红节点的子节点都为黑
4、从任一节点到其每个叶子的所有路径,都包含相同数目的黑色节点
设红黑树高度为h,非叶子节点数为n
则h<=2log2(n+1)

定义

红黑树也是一种自平衡二叉查找树,它与AVL树类似,都在添加和删除的时候通过旋转操作保持二叉树的平衡,以求更高效的查询性能。
与AVL树相比,红黑树牺牲了部分平衡性,以换取插入/删除操作时较少的旋转操作,整体来说性能要优于AVL树。它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目.

选择以二叉树为基础,在二叉树的属性中加入一个颜色属性来表示2-3-4树中不同的节点。
2-3-4树中的2节点对应着红黑树中的黑色节点,而2-3-4树中的非2节点是以红节点+黑节点的方式存在,
红节点的意义是与黑色父节点结合,表达着2-3-4树中的3,4节点。

去掉下图的红色节点,则会得到一棵四叉树

#define RED 1
#define BLACK 0
typedef struct node 
{
	bool color;// 记录节点颜色的 color 属性,暂定 true 表示红色
	int data;//数据
	struct node *right;
	struct node *left;
	struct node *parent;// 为了方便迭代插入,所需的 parent 属性
} RBNode;
typedef struct _rbtree 
{
	RBNode *root; // 当前红黑树的根节点,默认为 NULL
	RBNode *nil; // 叶子节点都指向 nil
} RBTree;

RBT类似属于一种空间换时间类型的优化,
在节点上,增加了颜色属性的数据,相当于增加了空间的消耗。通过颜色属性的增加 换取 后面平衡操作的次数减少。任何不平衡都会在三次旋转之内解决
虽然,红黑树没有像AVL树那样严格平衡,但是也需要一定的平衡,要符合“从任一节点到其每个叶子的所有路径,都包含相同数目的黑色节点”,
即黑色完美平衡。

常见方法(函数)
RBTree *init();//初始化一颗红黑树
void leftRotate(RBTree *T, RBNode *x);//左旋
void rightRotate(RBTree *T, RBNode *y);//右旋
void insert(RBTree *T, RBNode *z);//插入结点
void insertFix(RBTree *T, RBNode *z);// 插入之后需要调整树的结构
RBNode *deleteRBNode(RBTree *T, RBNode *z);//删除结点
void deleteRBNodeFix(RBTree *T, RBNode *x);// 删除之后需要调整树的结构
RBNode *RBTree_mini(RBTree *T, RBNode *x);// 找右子树中的最小节点
RBNode *RBTree_maxi(RBTree *T, RBNode *x);// 找左子树中的最大节点
RBNode *RBTree_successor(RBTree *T, RBNode *x);//儿子节点有两个孩子,用后继节点替
换待删除的节点
RBNode *RBTree_search(RBTree *T, int data);//查找
void RBTree_middleOrderTraverse(RBTree *T, RBNode *node);//中序遍历
1、左旋、右旋、变色(恢复平衡操作)

在这里插入图片描述

2、插入(添加)

默认插入节点为红色,因为父节点为黑色的概率较大,这样可以减少颜色冲突。
插入与二叉搜索树的插入操作一样,插入后,可能会出现失衡。
有以下情况

(1)插入的值已存在
不做任何操作
(2)树为空
直接把节点插入,并将该节点置为黑节点
(3)父节点为黑色
插入的节点为红色,不会产生颜色冲突,且不影响平衡,直接插入
(4)父节点为红色
违反性质“每个红节点的子节点必为黑色”
a、父节点和叔节点都为红色

将父节点和叔节点改为黑节点,祖父节点设为红节点。再对祖父节点进行判断是否失衡,如果失衡,则继续进行上述操作,直至平衡

b、父节点为红,叔节点为黑

此情况有四种情况:LL、LR、RR、RL失衡

LL失衡:新插入节点为父节点的左节点,父节点为祖父节点的左节点

平衡操作:将父节点变为黑色,祖父节点设为红色,对祖父节点进行右旋

LR失衡:新插入节点为父节点的右节点,父节点为祖父节点的左节点

平衡操作:对父节点进行左旋(变为LL失衡),将新父节点变为黑色,将祖父节点变为红色,再对祖父节点进行右旋

RR失衡:新插入节点为父节点的右节点,父节点为祖父节点的右节点

平衡操作:将父节点变为黑色,祖父节点变为红色,对祖父节点进行左旋

RL失衡:新插入节点为父节点的左节点,父节点为祖父节点的右节点

平衡操作:对父节点进行右旋操作,将新父节点变为黑色,将祖父节点变为红色,对祖父节点进行左旋操作

3、删除

删除叶子节点,可能违反性质“叶子节点必为黑色”和“从任一节点到其每个叶子的所有路径,都包含相同数目的黑色节点”
删除操作与二叉搜索树的操作一样,只是多了平衡处理

删除操作
a、删除节点为叶子节点

直接删除该节点

b、删除节点只有一个子节点

删除该节点,并使用子节点替代当前节点

c、删除节点有两个子节点

找出后继节点(即右节点的最左节点,右子树中最小的节点),删除当前节点,并用后继节点替代当前节点(直接复制节点值),在子树里删除后继节点

平衡处理
(1)删除节点为叶子节点

1)若为红节点,直接删除。

2)若为黑节点,
a、删除节点为根节点,则将该树置为空树

b、如果不是根节点,则其必有兄弟节点(如果没有兄弟节点,会失衡)

当兄弟节点为黑色(A-F)

A、兄弟节点为黑色,且子节点都为黑色,父节点为红色

则删去节点,然后将兄弟节点染为红色,父节点染为黑色。

B、兄弟节点为黑色,且子节点都为黑色,父节点为黑色

则删去节点,然后将兄弟节点染为红色。(此时因为父节点本身就是黑色,兄弟节点染为红色,会导致该树的黑节点少1,需要进行自平衡,向上染色递归处理)

C、兄弟节点为黑色,兄弟节点为父节点的左节点,且兄弟节点的左节点为红节点

则删去节点,然后交换父节点和兄弟节点的颜色,并将兄弟节点的左节点染为黑,然后对父节点进行右旋操作

D、兄弟节点为黑色,兄弟节点为父节点的右节点,且兄弟节点的右节点为红节点

则删去节点,然后交换父节点和兄弟节点的颜色,并将兄弟节点的右节点染为黑色,然后对父节点进行左旋操作

E、兄弟节点为黑色,兄弟节点为父节点的左节点,且兄弟节点的右节点为红节点

则交换兄弟节点和兄弟节点的右节点的颜色,对兄弟节点进行左旋操作,接下来按照C的操作即可。

F、兄弟节点为黑色,兄弟节点为父节点的右节点,且兄弟节点的左节点为红节点

则交换兄弟节点和兄弟节点的左节点的颜色,对兄弟节点进行右旋操作,接下来按照D的操作即可。

当兄弟节点为红色(G-),此时兄弟节点的两个子节点和父节点必为黑色

G、兄弟节点为红色,兄弟节点为父节点的左节点

Ga、交换兄弟节点和父节点的颜色,对父节点进行右旋操作,接下来按照A的操作即可

Gb、交换兄弟节点和父节点的颜色,对父节点进行右旋操作,然后交换父节点A和E的颜色,将F染为黑,对A右旋,删去C即可

Gc、交换-右旋,然后交换E和F的颜色,对父节点A右旋,删去节点C即可

H、兄弟节点为红色,兄弟节点为父节点的右节点

Ha、交换-左旋,然后交换父节点A和E的颜色,删去节点即可
图中左旋后的D和E位置错误了,正确的图应该将D、E调换位置

Hb、交换-左旋,然后交换父节点A和D的颜色,将节点F染为黑色,对父节点A进行左旋,删去节点C即可
在这里插入图片描述
Hc、交换-左旋,然后交换D和F的颜色,对父节点A进行左旋,删去节点C即可
在这里插入图片描述

红黑树详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值