【JAVA】红黑树 详解

红黑树

红黑树(Red-Black Tree)是 TreeMap 底层实现的一种自平衡二叉搜索树。红黑树的特性保证了在最坏情况下基本操作(如插入、删除、查找)的时间复杂度为 O(log n)。以下是红黑树的详细介绍,包括其性质、插入与删除操作、平衡调整机制等。

1. 红黑树的性质

红黑树是一种特殊的二叉搜索树,具有以下五个基本性质:

  1. 节点是红色或黑色:红黑树中的每个节点都是红色或黑色。
  2. 根节点是黑色:红黑树的根节点必须是黑色的。
  3. 叶节点(NIL节点)是黑色的:红黑树的每个叶节点(空节点,即null节点)都是黑色的。
  4. 红色节点的子节点必须是黑色:即在一条路径上,不能有两个连续的红色节点。红色节点的子节点可以是黑色节点或 null 节点。
  5. 从任一节点到其每个叶节点的所有路径都包含相同数量的黑色节点:这个属性确保了树的平衡性,避免了极端不平衡的情况。

这些性质保证了红黑树的平衡性,即树的最长路径不会超过最短路径的两倍,确保了查找、插入和删除操作的效率。

2. 红黑树的核心操作

红黑树的核心在于如何在插入和删除节点时保持树的平衡。为此,红黑树使用了旋转(Rotation)和重新着色(Recoloring)两种主要操作。

2.1 旋转(Rotation)

旋转是红黑树维护平衡性的重要操作。旋转分为两种:左旋和右旋。

  • 左旋(Left Rotation):左旋操作是将当前节点的右子节点变为其父节点,并将当前节点变为新的父节点的左子节点。
    例子:假设节点 x 需要进行左旋。
     x                        y
    / \                      / \
   a   y     ====>           x   c
      / \                  / \
     b   c                a   b
  • 右旋(Right Rotation):右旋操作是将当前节点的左子节点变为其父节点,并将当前节点变为新的父节点的右子节点。
    例子:假设节点 y 需要进行右旋。
       y                        x
      / \                      / \
     x   c    ====>           a   y
    / \                          / \
   a   b                        b   c

2.2 插入操作

当一个新节点插入红黑树时,它初始为红色。插入操作后,红黑树可能会违反其性质,需要通过旋转和重新着色来恢复红黑树的性质。
插入的步骤:

  1. 普通的二叉搜索树插入:将节点插入到红黑树中,初始颜色为红色。插入位置遵循二叉搜索树的规则。
  2. 调整树结构:根据插入节点的位置,可能会违反红黑树的性质,因此需要对树进行调整。根据插入节点的父节点、叔叔节点的颜色,有以下几种情况:
    • 情况 1: 插入节点的父节点是黑色:不需要做任何调整,因为树的性质没有被破坏。
    • 情况 2: 插入节点的父节点是红色,且叔叔节点也是红色:将父节点和叔叔节点涂黑,将祖父节点涂红,然后将祖父节点作为新的插入节点,继续检查。
    • 情况 3: 插入节点的父节点是红色,但叔叔节点是黑色:
      • 左左情况:父节点是祖父节点的左子节点,插入节点是父节点的左子节点。右旋祖父节点,交换父节点和祖父节点的颜色。
      • 左右情况:父节点是祖父节点的左子节点,插入节点是父节点的右子节点。左旋父节点,形成左左情况,再进行右旋和颜色交换。
      • 右右情况:父节点是祖父节点的右子节点,插入节点是父节点的右子节点。左旋祖父节点,交换父节点和祖父节点的颜色。
      • 右左情况:父节点是祖父节点的右子节点,插入节点是父节点的左子节点。右旋父节点,形成右右情况,再进行左旋和颜色交换。
  3. 调整完成后:确保根节点为黑色,以维护红黑树的性质。

2.3 删除操作

删除节点的操作比插入复杂得多,因为删除节点可能会导致多种红黑树性质被破坏。删除操作同样依赖旋转和重新着色来恢复平衡。
删除的步骤:

  1. 找到并删除节点:根据二叉搜索树的删除规则找到并删除目标节点。如果被删除的节点有两个子节点,则用其直接后继节点替换该节点,然后删除后继节点。
  2. 调整树结构:删除节点后,红黑树可能会违反其性质,因此需要进行调整。
    • 情况 1: 删除的节点是红色:如果删除的是红色节点,红黑树的性质不会被破坏,不需要做额外操作。
    • 情况 2: 删除的节点是黑色,且其唯一的子节点是红色:将子节点涂黑即可,树的性质不会被破坏。
    • 情况 3: 删除的节点是黑色,且其子节点是黑色:此时,需要根据兄弟节点的颜色及其子节点的颜色进行复杂的调整,包括旋转和重新着色。主要有以下几种情况:
      • 兄弟节点是红色:将兄弟节点涂黑,父节点涂红,然后以父节点为中心左旋或右旋。
        兄弟节点是黑色且两个子节点都是黑色:将兄弟节点涂红,重新将父节点视为新的被删除节点,继续检查。
      • 兄弟节点是黑色,兄弟的一个子节点是红色:根据兄弟子节点的颜色进行旋转和重新着色。
      • 确保根节点为黑色:删除调整完成后,必须确保根节点为黑色,以维持红黑树的性质。

3. 代码实现

红黑树的核心操作在 TreeMap 的源码中体现得非常清晰。以下是红黑树在 TreeMap 中的几个关键方法。
3.1 插入操作的调整代码 (fixAfterInsertion)

private void fixAfterInsertion(Entry<K,V> x) {
    x.color = RED;

    while (x != null && x != root && x.parent.color == RED) {
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {
                if (x == rightOf(parentOf(x))) {
                    x = parentOf(x);
                    rotateLeft(x);
                }
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateRight(parentOf(parentOf(x)));
            }
        } else {
            Entry<K,V> y = leftOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {
                if (x == leftOf(parentOf(x))) {
                    x = parentOf(x);
                    rotateRight(x);
                }
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateLeft(parentOf(parentOf(x)));
            }
        }
    }
    root.color = BLACK;
}

分析:

  • 插入节点后,通过 while 循环检查并调整树的平衡性。
  • 如果父节点是红色,可能会违反红黑树的性质,需要进行调整。
  • 根据父节点的位置和兄弟节点的颜色,通过旋转和重新着色来恢复平衡。

3.2 删除操作的调整代码 (fixAfterDeletion)

private void fixAfterDeletion(Entry<K,V> x) {
    while (x != root && colorOf(x) == BLACK) {
        if (x == leftOf(parentOf(x))) {
            Entry<K,V> sib = rightOf(parentOf(x));

            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateLeft(parentOf(x));
                sib = rightOf(parentOf(x));
            }

            if (colorOf(leftOf(sib)) == BLACK &&
                colorOf(rightOf(sib)) == BLACK) {
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                if (colorOf(rightOf(sib)) == BLACK) {
                    setColor(leftOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateRight(sib);
                    sib = rightOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(rightOf(sib), BLACK);
                rotateLeft(parentOf(x));
                x = root;
            }
        } else { // symmetric
            Entry<K,V> sib = leftOf(parentOf(x));

            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                rotateRight(parentOf(x));
                sib = leftOf(parentOf(x));
            }

            if (colorOf(rightOf(sib)) == BLACK &&
                colorOf(leftOf(sib)) == BLACK) {
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                if (colorOf(leftOf(sib)) == BLACK) {
                    setColor(rightOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateLeft(sib);
                    sib = leftOf(parentOf(x));
                }
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(leftOf(sib), BLACK);
                rotateRight(parentOf(x));
                x = root;
            }
        }
    }

    setColor(x, BLACK);
}

分析:

  • 删除节点后,通过 while 循环检查并调整树的平衡性。
  • 根据兄弟节点的颜色以及兄弟节点的子节点的颜色,通过旋转和重新着色来恢复红黑树的性质。

4. 红黑树的优势

红黑树作为一种自平衡二叉搜索树,具有以下几个明显的优势:

  • 平衡性:红黑树在最坏情况下也能保持近似平衡,确保了操作的时间复杂度为 O(log n)。
  • 插入和删除效率高:相比于 AVL 树,红黑树在插入和删除操作上更为高效,因为它不需要频繁地进行旋转。
  • 实现简单:红黑树的代码相对简单,易于实现和维护。

5. 应用场景

红黑树广泛应用于需要快速插入、删除、查找,并且数据需要有序的场景。例如:

  • 关联数组(键值对):如 TreeMap 和 TreeSet。
  • 内核中的调度器:许多操作系统内核使用红黑树来实现调度器,以便快速找到优先级最高的任务。
  • 数据库中的索引:一些数据库系统使用红黑树作为索引结构,支持快速的数据检索和更新。

通过对红黑树及其在 TreeMap 中的实现细节的深入了解,开发者可以更好地理解 TreeMap 的工作原理,以及如何利用红黑树的特性进行高效的数据管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值