数据结构:红黑树的旋转原理和模拟实现

本文详细介绍了红黑树的旋转原理,包括红黑树的特点、节点定义及插入过程。插入时,根据节点颜色和父节点、叔叔节点颜色的不同情况,进行了情况一至情况三的分析,并给出了相应的旋转调整策略,确保红黑树性质得以维持。同时,文章提到了红黑树在实际应用中相对于AVL树的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

红黑树的旋转原理和模拟实现

我们了解到AVL树虽然效率很高,但是它是通过多次的旋转才到达一个绝对的平衡,旋转的消耗其实也很大。因此开始引入近似平衡的一棵树----红黑树(RBTree)。红黑树每一个节点不是红色的就是黑色的,它保证了最长路径不超过最短路径的二倍。

其实一般来说使用红黑树会比AVL树更多,因为虽然AVL树是更加平衡的,但是它的平衡是通过更多次的旋转得到的,旋转的时候消耗还是很大的。而红黑树是近似平衡的,它的旋转比AVL树要少。但是有人可能会有疑问,那红黑树的搜索效率没有AVL树高啊?其实他俩是差不多的,因为AVL树是O(logN),而红黑树是O(2logN),假如是十亿个数据的话,AVL树用30次查找,那么红黑树也就是60次查找,对于现在的计算机CPU计算速度来说是没有多大影响的,所以用红黑树用的会多一些。

红黑树的特点:
  1. 根节点一定是黑色的
  2. 一个红色节点的孩子只能是黑色的
  3. 每一条路径上的黑色节点的个数相同
  4. 每个节点不是红色的就是黑色的
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

根据这五个特点我们就可以推断出最长路径不超过最短路径的二倍。因为如果一颗树是红黑树,那么它的最短路径就是全黑节点的那一条路径,最长路径就是一个黑色一个红色这样红黑相间的路径。

红黑树节点的定义

enum Color//枚举颜色
{
   
   
	RED,
	BLACK,
};
template<class K,class V>
struct RBTreeNode
{
   
   
	RBTreeNode<K, V>* _left;//指向左树节点
	RBTreeNode<K, V>* _right;//指向右树节点
	RBTreeNode<K, V>* _parent;//指向父亲节点
    pair<K, V> _kv;
	Color _col;//颜色
};

红黑树的插入

红黑树的插入需要这么几步:

  1. 如果是空树,那么直接让root等于这个新开节点,然后把这个节点的颜色变为黑色
  2. 如果不是空树,那么就先去找要插入的位置,如果要插入的值比该节点小就去左边,比该节点大就去右边
  3. 找到位置之后将该节点连接到树中,然后将这个新插入的节点设置为红色(为什么是设置为红色呢? 因为如果插入一个黑色节点的话,那么就会破坏红黑树的每条路径上的黑色节点数目相同的这条性质,因为在你插入的这条路径上就会多出来一个黑色节点,那么就会影响到其他的路径,影响范围很大。但是如果插入的是红色节点,那么如果它的父亲是黑色就非常完美,不用对树进行修改,只有当它的父亲是红色才需要进行修改,因为两个红色的节点不能连接再一起。所以说如果插入黑色节点,必然一定会破坏所以路径,而插入红色的节点是有可能会破坏这条路径。)
  4. 如果插入这个红色节点之后父亲也是红色,则对这颗树进行调整

如果要进行调整的的话分为以下几种情况,我们约定一下起名规则如下:
cur:新插入的节点
parent:新插入节点的父亲节点
uncle:新插入节点的叔叔节点
grandfather:新插入节点的祖父节点

情况一:插入节点为红色,父亲为红色,叔叔存在且为红色

如下图,新插入节点cur是红色,父亲也是红色,叔叔也是红色,那么祖父必然是一个黑色节点,那么把父亲和叔叔变成黑色,把祖父变成红色,然后这颗树上每条路径的黑色节点就都是一样的了。
在这里插入图片描述
但是这个步骤完成之后还需要继续往上判断,因为如果把祖父变成了红色,祖父还不是根节点的话,万一祖父的父亲也是红色节点就需要再次调整了,如下图。
在这里插入图片描述
最后根据需要,如果grandfather为根节点的话直接把根变成黑色(因为根节点一定是黑色的)。

情况二:插入节点(左边)为红色,父亲为红,叔叔不存在/叔叔为黑

如果插入节点红色,父亲也是红色,那么祖父一定是黑色,此时如果没有叔叔节点的话,那就要进行旋转,如图情况,对这颗树进行右单旋,然后在进行变色,把祖父变成红色,把父亲变成黑色。
在这里插入图片描述
如果插入节点红色,父亲也是红色,那么祖父一定是黑色,此时如果叔叔节点为黑,那么应该怎么办呢?如果遇到叔叔节点为黑时,那一定是通过情况一演变上来的。如下图,当向上更新时,发现叔叔节点为黑,那么我们以parent为中心进行右单旋,把parent的左树变成grandfather的左树,然后再把grandfather变成parent的右树。

在这里插入图片描述

情况三:插入节点(右边)为红色,父亲为红,叔叔不存在/叔叔为黑

插入节点为红,父亲如果为红,祖父必然为黑,如果此时叔叔节点不存在,那么需要先以parent为中心进行左单旋,然后再交换cur和parent的指向(遵循我们制定的约定),然后再以grandfather为中心右单旋。
在这里插入图片描述
如果插入节点为红,父亲为红,祖父必然为红,此时叔叔节点为黑,那么必然是情况一演变而来的,首先以parent为中心进行左单旋,然后再交换parent和cur的指向,再以grandfather为中心进行右单旋,最后再讲parent变为黑色,grandfather变为红色。如下图:
在这里插入图片描述
注:以上博主画的都是父亲在左边叔叔在右边的情况,反之叔叔在左边父亲在右边也是一样的啦
然后博主就不画了吧 |ू・ω・` ),画图太费眼睛啦!博主上面的图画了好久好久!
博主可能有强迫症!然后哇的一声就哭了 ヘ(;´Д`ヘ)

红黑树的左单旋右单旋和AVL树的左单旋右单旋一毛一样,还有遍历也是一毛一样!!!

好的!那么我们一起来看代码 ~ 嘻嘻嘻 ~

RBTree.h
#pragma once

#include <iostream>
using namespace std;

enum Color
{
   
   
	RED,
	BLACK,
};

template<class K,class V>
struct RBTreeNode
{
   
   
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值