红黑树(Red-Black Tree),为什么需要红黑树?红黑树的核心特性(5 条规则)?黑色高度?平衡修复?红黑树旋转变色的具体操作案例?

红黑树(Red-Black Tree),为什么需要红黑树?红黑树的核心特性(5 条规则)?黑色高度?平衡修复?红黑树旋转变色的具体操作案例?

红黑树(Red-Black Tree)是一种自平衡二叉搜索树(Self-Balancing Binary Search Tree),它通过一套特定的规则(颜色标记 + 旋转操作)维持树的平衡性,确保插入、删除、查找等操作的时间复杂度始终保持在O(log n)(n 为节点数)。与 AVL 树相比,红黑树的平衡条件更宽松,旋转操作更少,在实际应用中(如 C++ 的std::map、Java 的TreeMap)更为广泛。


为什么需要红黑树?

普通的二叉查找树在最坏情况下(例如,插入的元素已经有序)会退化成一个链表,导致查找、插入和删除操作的时间复杂度退化为 O(n)。而红黑树通过上述规则保证了树的高度始终保持在 O(log n) 的水平,从而使得查找、插入和删除操作的最坏情况时间复杂度均为 O(log n)。


红黑树的核心特性(5 条规则)

红黑树的每个节点都有一个 “颜色” 属性(红色或黑色),且必须满足以下 5 条规则。

(1)节点颜色

每个节点要么是红色,要么是黑色。

(2)根节点

根节点必须是黑色。

(3)叶子节点

所有叶子节点(NIL 节点,即空节点)都是黑色。(注:实际实现中,叶子节点通常被简化为一个共享的 NIL 节点,不存储数据)。

(4)红色节点的子节点

如果一个节点是红色,那么它的两个子节点必须是黑色(即不允许两个红色节点直接相连)。

(5)路径黑色性(黑色高度平衡)

从任意节点到其所有叶子节点的路径中,包含的黑色节点数量相同(称为 “黑色高度”)。


红黑树节点的定义

(1)颜色属性

使用RED和BLACK两种状态,新节点默认设为红色(减少插入时对黑色高度的影响)。

(2)指针设计

①左右子节点指针用于维持二叉搜索树的结构。
②父节点指针是红黑树特有的(普通 BST 可省略),用于在旋转和平衡调整时快速定位祖先节点。

(3)叶子节点处理

实际实现中,通常将null子节点视为 “哨兵节点”(NIL 节点),统一设为黑色,简化边界条件判断。
哨兵节点可以是一个全局共享的实例,所有空指针都指向它。

// 定义节点颜色的枚举
enum Color {
    RED, BLACK
}

// 红黑树节点类
class RedBlackTreeNode {
    int value;                // 节点存储的值(可以是任意可比较类型)
    Color color;              // 节点颜色(红/黑)
    RedBlackTreeNode left;    // 左子节点引用
    RedBlackTreeNode right;   // 右子节点引用
    RedBlackTreeNode parent;  // 父节点引用(便于旋转和调整)

    // 构造方法
    public RedBlackTreeNode(int value) {
        this.value = value;
        this.color = Color.RED;  // 新节点默认红色(插入操作的约定)
        this.left = null;
        this.right = null;
        this.parent = null;
    }
}

红黑树为何能保持平衡?

上面这 5 条规则共同保证了红黑树的 “近似平衡”。

最长路径(红黑交替)的长度不会超过最短路径(全黑)的 2 倍。因此,树的高度被限制在 2log(n+1) 以内,确保了所有操作的对数级时间复杂度。


关键概念:黑色高度

在红黑树中,黑色高度(Black Height) 是一个核心概念,用于衡量从某节点到其所有叶子节点的路径中黑色节点的数量,是维持树平衡的关键指标之一。

黑色高度的定义?

(1)对于任意节点,其黑色高度是指从该节点出发,到达其任意一个叶子节点(NIL 节点)的路径中,黑色节点的总数(包含该节点自身)。
(2)叶子节点(NIL 节点,空节点)被定义为黑色,其黑色高度为 1(自身)。
(3)红色节点的黑色高度与其父节点、子节点的黑色高度相关,但自身不增加黑色高度的计数。

红黑树规则明确规定:从任意节点到其所有叶子节点的路径中,包含的黑色节点数量必须相同。

意味着:
(1)任意节点的左子树和右子树的黑色高度必须相等(否则从该节点到左右叶子的黑色节点数会不同)。
(2)根节点的黑色高度决定了整棵树的黑色高度(从根到所有叶子的黑色节点数相同)。


插入操作与平衡修复

插入新节点时,默认将其标记为红色(减少对黑色高度的影响),然后检查是否违反红黑树规则。若违反,通过以下两种操作修复:

(1)旋转操作(与 AVL 树类似)

①左旋转
将右子树的根节点提升为父节点,原父节点变为其左子树。

②右旋转
将左子树的根节点提升为父节点,原父节点变为其右子树。

旋转不改变二叉搜索树的性质(左子树值 < 父节点值 < 右子树值),仅调整结构。

(2)变色操作

改变节点的颜色(红→黑或黑→红),用于调整红色节点的分布,避免违反规则。

①新节点默认染为 红色(若染为黑色,会直接破坏规则 5:黑色高度平衡,修复更复杂)。
②插入后可能违反规则 2(根节点必须是黑色)(根为红)或规则 4(不允许两个红色节点直接相连)(连续红节点),需分情况处理:
(1)若父节点是黑色:无冲突,无需调整。
(2)若父节点是红色(必存在黑色祖父节点,因规则 4:不允许两个红色节点直接相连
      若 “叔叔节点”(父节点的兄弟)是红色:通过重新着色(父、叔变黑,祖父变红)修复。
      若 “叔叔节点” 是黑色:通过旋转(左旋 / 右旋)+ 着色修复。

(3)删除操作的调整

删除操作更复杂,因可能删除黑色节点,破坏规则 5(黑色高度平衡)。核心是处理 “双黑节点”(删除黑色节点后,其子节点继承 “额外黑色”),通过旋转和着色消除双黑状态。


红黑树 vs AVL 树

特性红黑树AVL 树
平衡条件颜色规则 + 黑色高度相等平衡因子绝对值≤1(严格平衡)
旋转频率低(插入最多 2 次,删除最多 3 次)高(插入 / 删除可能多次旋转)
空间开销存储颜色(1 bit)存储平衡因子(通常 1 byte)
适用场景频繁插入 / 删除(如映射、缓存)频繁查找(如数据库索引)

红黑树与AVL树都是高效的平衡二叉树,增删改查时间复杂度都是O(log_{2}^{N}),红黑树不追求绝对平衡,只需保证最长路径不超过最短路径的两倍即可,相对而言,降低了插入和旋转的次数,所以经常在增删改查的结构中性别比AVL树更好,且红黑树的实现相对来说更简单,实际运用中也更多。


红黑树应用场景

红黑树因高效的插入删除性能,被广泛应用于:

(1)C++ 的 std::map、std::set
(2)Java 的 TreeMap、TreeSet
(3)Linux 内核的进程调度、虚拟内存管理
(4)数据库索引等需要动态维护有序数据的场景

红黑树通过颜色规则和少量旋转,在保证二叉搜索树特性的同时,实现了近似平衡,是兼顾效率与实现复杂度的优秀数据结构。


红黑树旋转变色的具体操作案例

下面从空树开始,逐步演示红黑树的插入、旋转和平衡调整过程,每一步都详细说明操作内容和原因。

(1)初始状态:空树

(2)插入第一个节点(值 = 10)

操作:插入新节点 10
规则处理:新节点默认为红色,但根据规则 2(根节点必须为黑色),将其改为黑色。

在这里插入图片描述

(3)插入第二个节点(值 = 5)

操作:按 BST 规则,5<10,插入为左子节点,默认为红色。
检查规则:无违反(根为黑,无连续红节点)。

在这里插入图片描述

(4)插入第三个节点(值 = 15)

操作:15>10,插入为右子节点,默认为红色
检查规则:无违反(10 为黑,15 为红,无连续红)

在这里插入图片描述

(5)插入第四个节点(值 = 3)

操作:3<5,插入为 5 的左子节点,默认为红色
检查规则:父节点 5 是红色,新节点 3 也是红色,违反规则 4(连续红节点)

家族关系:
(1)新节点 3 ®
(2)父节点 5 ®
(3)祖父节点 10 (B)
(4)叔叔节点 15 ®(叔叔为红色)

调整步骤:
(1)将父节点 5 和叔叔节点 15 改为黑色
(2)将祖父节点 10 改为红色
(3)因 10 是根节点,需再改回黑色(符合规则 2)

调整后结果:
在这里插入图片描述

(6)插入第五个节点(值 = 1)

操作:1<3,插入为 3 的左子节点,默认为红色
检查规则:父节点 3 是红色,新节点 1 也是红色,违反规则 4

家族关系:
(1)1®,3®
(2)5 (B),NIL (B)(叔叔为空,视为黑色)

结构:5 左→ 3左→ 1(左左结构)

调整步骤:
(1)右旋祖父节点 5
(2)将原父节点 3 改为黑色
(3)将原祖父节点 5 改为红色

调整过程:
(1)右旋前(违反规则 4)
在这里插入图片描述
(2)右旋后(未着色)
在这里插入图片描述
(3)右旋后(着色后)
在这里插入图片描述

(7)插入第六个节点(值=7)

操作:7>5,插入为5的右子节点,默认为红色
检查规则:父节点5是红色,新节点7也是红色,违反规则4

家族关系
(1)7®,5®
(2)3(B),1®(叔叔为红色)

调整步骤
(1)将父节点5和叔叔节点1改为黑色
(2)将祖父节点3改为红色

调整后结果
在这里插入图片描述

(8)插入第七个节点(值=12)

新节点12®,父节点15(B),无冲突。
在这里插入图片描述

(9)插入第八个节点(值=18)

操作:18>15,插入为15的右子节点,默认为红色
检查规则:父节点15是黑色,无违反规则

结果
在这里插入图片描述

(10)插入第九个节点(值=17)

(1)17<18,为18的左子节点
(2)新节点17®,父节点18®,连续红色(违反规则4)
(3)祖父15(B),叔叔12®(叔叔为红)

调整:父18、叔12改黑,祖父15改红 。
在这里插入图片描述

(11)插入第十个节点(值=20)

  • 操作:20>18,插入为18的右子节点,默认为红色
  • 检查规则:父节点18是黑色,无违反规则
  • 最终结果
    在这里插入图片描述

总结操作要点

(1)插入原则:新节点默认为红色,减少对黑色高度的影响
(2)调整时机:仅当插入后出现连续红色节点时需要调整
(3)调整策略
①叔叔为红:通过着色解决(父叔变黑,祖父变红)
②叔叔为黑:通过旋转+着色解决(根据结构左旋/右旋)
(4)根节点保障:任何时候根节点必须为黑色

通过以上步骤可以看到,从空树开始,红黑树通过自动平衡机制,始终保持着近似平衡的结构,确保所有操作的时间复杂度为O(log n)。

总图:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值