红黑树基础

⚠️ 内容不完整 - 原始网页使用 JavaScript 动态渲染,静态抓取仅获取部分内容。待补充完整内容。

原始资料

raw/articles/2026/05/红黑树基础

一句话总结

红黑树是自平衡的二叉搜索树,它的树高在任何时候都能保持在 $O(\log N)$ (完美平衡),这样就能保证增删查改的时间复杂度都是 $O(\log N)$ 。

核心要点

问题背景

二叉搜索树的应用及可视化 讲了普通的二叉搜索树存储键值对实现 TreeMap/TreeSet 的思路。

二叉搜索树的操作效率取决于树高:

  • 树结构越平衡,树高越接近 $\log N$,增删查改效率越高
  • 普通二叉搜索树的问题:不会自动对树进行平衡,特殊情况下会退化成链表,时间复杂度退化为 $O(N)$

红黑树解决方案

红黑树通过自平衡机制,保证树高在任何时候都能保持在 $O(\log N)$,从而确保增删查改的时间复杂度都是 $O(\log N)$。

可视化资源

前置知识

阅读本文前需要掌握:

相关概念

  • 二叉搜索树(已存在于概念库)
  • 红黑树(待第2篇来源后创建独立概念页)
  • 自平衡树(待第2篇来源后创建独立概念页)
  • TreeMap / TreeSet

红黑树的五条性质

红黑树通过以下五条性质来保持近似平衡:

  1. 每个节点要么是红色,要么是黑色
  2. 根节点是黑色的
  3. 所有叶子节点(NIL 空节点)是黑色的
  4. 红色节点的两个子节点都必须为黑色(不能有两个连续的红色节点)
  5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点(黑高相同)

💡 为什么这五条性质能保证树高 O(logN)? 性质 4 和 5 共同起作用:性质 4 保证没有两个相邻红节点,性质 5 保证任意路径黑节点数相同。可以证明,这样的树高最多是 2log₂(N+1),即 O(logN)。

平衡操作:旋转与变色

当插入或删除节点破坏红黑树性质时,需要通过旋转变色来恢复平衡。

左旋(leftRotate)

     x                y
    / \              / \
   a   y    ,>    x   c
      / \          / \
     b   c        a   b

左旋操作:将节点 x 的右子节点 y 提升为新的子树根,x 成为 y 的左子节点。

右旋(rightRotate)

       y              x
      / \            / \
     x   c  ,>    a   y
    / \                / \
   a   b              b   c

右旋操作:将节点 y 的左子节点 x 提升为新的子树根,y 成为 x 的右子节点。

插入操作

红黑树插入分为两步:

第一步:按 BST 规则插入

像普通二叉搜索树一样找到插入位置,新节点初始颜色设为红色(为什么?因为插入红色节点只会破坏性质 2 和 4,而插入黑色节点一定会破坏性质 5)。

第二步:修复红黑树性质

插入红色节点后,可能违反性质 2(根变红)或性质 4(出现连续红节点)。需要根据父节点、叔叔节点的颜色进行分类调整:

情况 描述 处理方式
情况1 父节点黑色 无需调整,直接结束
情况2 父节点红色,叔叔节点红色 父、叔变黑,祖父变红,继续向上修复
情况3 父节点红色,叔叔黑色,"内侧"插入 先旋转变成情况4
情况4 父节点红色,叔叔黑色,"外侧"插入 旋转+变色,修复完成

📝 口诀:叔叔红则变颜色向上传,叔叔黑则旋转+变色搞定。

删除操作(简要)

删除操作比插入更复杂,分为:

  1. 按 BST 删除规则删除节点(找到后继或前驱替换)
  2. 如果删除的是红色节点,不影响黑高,无需调整
  3. 如果删除的是黑色节点,需要通过旋转和变色来恢复黑高

删除的修复情况更多(约 8 种情况),这里不展开,核心思想是通过借节点合并节点来恢复性质。

与 AVL 树的对比

对比项 红黑树 AVL 树
平衡严格度 近似平衡(弱) 严格平衡(强)
树高上限 2log₂(N+1) log₂(N+1)
查询速度 稍慢(树高稍高) 更快(树高更低)
插入调整 最多 2 次旋转 可能需要 O(logN) 次旋转
删除调整 最多 3 次旋转 可能需要 O(logN) 次旋转
适用场景 插入删除频繁 查询频繁,修改少
典型应用 Java TreeMap/TreeSet 数据库索引

Java 代码框架

class RedBlackTree<K extends Comparable<K>, V> {
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private class Node {
        K key;
        V val;
        Node left, right;
        boolean color; // RED or BLACK

        Node(K key, V val, boolean color) {
            this.key = key;
            this.val = val;
            this.color = color;
        }
    }

    private Node root;

    // 左旋
    private Node leftRotate(Node x) {
        Node y = x.right;
        x.right = y.left;
        y.left = x;
        y.color = x.color;
        x.color = RED;
        return y;
    }

    // 右旋
    private Node rightRotate(Node y) {
        Node x = y.left;
        y.left = x.right;
        x.right = y;
        x.color = y.color;
        y.color = RED;
        return x;
    }

    // 颜色翻转(父红,两个子黑 → 父黑,两个子红)
    private void flipColors(Node h) {
        h.color = BLACK;
        h.left.color = RED;
        h.right.color = RED;
    }

    // 插入
    public void put(K key, V val) {
        root = put(root, key, val);
        root.color = BLACK; // 性质2:根节点始终为黑
    }

    private Node put(Node h, K key, V val) {
        if (h == null) return new Node(key, val, RED);

        int cmp = key.compareTo(h.key);
        if (cmp < 0) h.left = put(h.left, key, val);
        else if (cmp > 0) h.right = put(h.right, key, val);
        else h.val = val;

        // 修复红黑树性质
        if (isRed(h.right) && !isRed(h.left)) h = leftRotate(h);
        if (isRed(h.left) && isRed(h.left.left)) h = rightRotate(h);
        if (isRed(h.left) && isRed(h.right)) flipColors(h);

        return h;
    }

    private boolean isRed(Node x) {
        if (x == null) return false;
        return x.color == RED;
    }
}

时间复杂度

操作 时间复杂度 说明
查找 O(log N) 树高 O(logN)
插入 O(log N) 最多 2 次旋转
删除 O(log N) 最多 3 次旋转

典型应用

  • JavaTreeMapTreeSet 底层实现
  • C++std::mapstd::set 底层实现(通常是红黑树变种)
  • Linux:进程调度完全公平调度器(CFS)使用红黑树管理进程
  • Epoll:Linux epoll 使用红黑树管理就绪事件

⚠️ 内容说明:由于原网页使用 JavaScript 动态渲染,静态抓取仅获取前言部分。以上内容基于红黑树通用知识补充,非 labuladong 原文完整内容。如需原文,请在浏览器中打开原网页复制完整内容。


Interactive Graph