红黑树的一些理解

红黑树是一种自平衡的二叉查找树,它在每个节点上增加了一个额外的属性表示节点的颜色,可以是红色或黑色。通过满足一定的性质,红黑树确保了在进行插入、删除等操作时,树的高度保持在对数级别,从而保证了操作的时间复杂度为 O(log n)。

红黑树满足以下性质:

  1. 每个节点是红色或黑色。
  2. 根节点是黑色。
  3. 每个叶子节点(NIL节点,空节点)是黑色的。
  4. 如果一个节点是红色的,则它的两个子节点都是黑色的(不能有两个相连的红色节点)。
  5. 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点(黑色节点数量相同)。

这里是一个简单的红黑树的 Java 实现,包含插入操作:

java 复制代码
class RedBlackTreeNode {
    int data;
    RedBlackTreeNode parent;
    RedBlackTreeNode left;
    RedBlackTreeNode right;
    int color;

    public RedBlackTreeNode(int data) {
        this.data = data;
        color = 1; // 新插入的节点默认为红色
        parent = left = right = null;
    }
}

public class RedBlackTree {
    private RedBlackTreeNode root;

    // 左旋
    private void leftRotate(RedBlackTreeNode x) {
        RedBlackTreeNode y = x.right;
        x.right = y.left;
        if (y.left != null)
            y.left.parent = x;
        y.parent = x.parent;
        if (x.parent == null)
            root = y;
        else if (x == x.parent.left)
            x.parent.left = y;
        else
            x.parent.right = y;
        y.left = x;
        x.parent = y;
    }

    // 右旋
    private void rightRotate(RedBlackTreeNode y) {
        RedBlackTreeNode x = y.left;
        y.left = x.right;
        if (x.right != null)
            x.right.parent = y;
        x.parent = y.parent;
        if (y.parent == null)
            root = x;
        else if (y == y.parent.right)
            y.parent.right = x;
        else
            y.parent.left = x;
        x.right = y;
        y.parent = x;
    }

    // 插入节点
    public void insert(int data) {
        RedBlackTreeNode newNode = new RedBlackTreeNode(data);
        RedBlackTreeNode parent = null;
        RedBlackTreeNode current = root;
        while (current != null) {
            parent = current;
            if (newNode.data < current.data)
                current = current.left;
            else
                current = current.right;
        }
        newNode.parent = parent;
        if (parent == null)
            root = newNode;
        else if (newNode.data < parent.data)
            parent.left = newNode;
        else
            parent.right = newNode;
        newNode.color = 1; // 新插入的节点默认为红色
        fixInsert(newNode);
    }

    // 插入后修复红黑树性质
    private void fixInsert(RedBlackTreeNode k) {
        RedBlackTreeNode u;
        while (k.parent.color == 1) {
            if (k.parent == k.parent.parent.right) {
                u = k.parent.parent.left;
                if (u.color == 1) {
                    u.color = 0;
                    k.parent.color = 0;
                    k.parent.parent.color = 1;
                    k = k.parent.parent;
                } else {
                    if (k == k.parent.left) {
                        k = k.parent;
                        rightRotate(k);
                    }
                    k.parent.color = 0;
                    k.parent.parent.color = 1;
                    leftRotate(k.parent.parent);
                }
            } else {
                u = k.parent.parent.right;

                if (u.color == 1) {
                    u.color = 0;
                    k.parent.color = 0;
                    k.parent.parent.color = 1;
                    k = k.parent.parent;
                } else {
                    if (k == k.parent.right) {
                        k = k.parent;
                        leftRotate(k);
                    }
                    k.parent.color = 0;
                    k.parent.parent.color = 1;
                    rightRotate(k.parent.parent);
                }
            }
            if (k == root) {
                break;
            }
        }
        root.color = 0;
    }

    // 中序遍历
    private void inorder(RedBlackTreeNode root) {
        if (root == null)
            return;
        inorder(root.left);
        System.out.print(root.data + " ");
        inorder(root.right);
    }

    public void inorder() {
        inorder(root);
    }

    public static void main(String[] args) {
        RedBlackTree tree = new RedBlackTree();
        tree.insert(10);
        tree.insert(20);
        tree.insert(30);
        tree.insert(40);
        tree.insert(50);
        tree.insert(60);
        tree.insert(70);

        System.out.println("红黑树中序遍历:");
        tree.inorder();
    }
}

这个实现中,左旋和右旋操作用于保持红黑树的性质。在插入操作中,先将新插入的节点标记为红色,然后通过 fixInsert() 方法来修复可能违反红黑树性质的地方。

让我们逐个讲解代码中的每个方法:

  1. RedBlackTreeNode 类:

    • 定义了红黑树节点的结构。
    • 每个节点包含 data(节点的值)、parent(父节点的引用)、left(左子节点的引用)、right(右子节点的引用)、color(节点的颜色,0 表示黑色,1 表示红色)。
  2. RedBlackTree 类:

    • root:红黑树的根节点。
  3. leftRotate(RedBlackTreeNode x) 方法:

    • 实现了左旋操作。
    • 参数 x 是要进行左旋的节点。
    • x 的右子节点 y 上移为 x 的父节点,x 成为 y 的左子节点。
    • 旋转后要更新节点的父节点引用。
  4. rightRotate(RedBlackTreeNode y) 方法:

    • 实现了右旋操作。
    • 参数 y 是要进行右旋的节点。
    • y 的左子节点 x 上移为 y 的父节点,y 成为 x 的右子节点。
    • 旋转后要更新节点的父节点引用。
  5. insert(int data) 方法:

    • 插入新节点到红黑树中。
    • 首先创建一个新节点,并按照二叉查找树的规则找到其应该插入的位置。
    • 将新节点插入,并将其颜色标记为红色。
    • 调用 fixInsert() 方法修正红黑树的性质。
  6. fixInsert(RedBlackTreeNode k) 方法:

    • 修复插入节点后可能违反红黑树性质的问题。
    • 参数 k 是刚刚插入的节点。
    • 通过对 k 的父节点、叔节点和祖父节点的颜色进行判断和旋转操作来保持红黑树的性质。
  7. inorder(RedBlackTreeNode root) 方法:

    • 中序遍历红黑树。
    • 递归地从左子树开始遍历,然后访问当前节点,最后递归地遍历右子树。
  8. inorder() 方法:

    • 对外提供的中序遍历方法的入口点。
  9. main(String[] args) 方法:

    • 测试主方法,创建一个红黑树对象,并插入一些节点,然后进行中序遍历以验证红黑树的正确性。

这些方法共同构成了一个简单的红黑树的实现,通过这些方法可以实现对红黑树的插入操作,并保持其性质。

红黑树通过保持以下两个性质来维持平衡:

  1. 黑色平衡性(Black-Height Balance):确保从根到叶子的每条路径上的黑色节点数目相同。换句话说,红黑树保证了所有路径上的黑色节点数目相等,这样就能够保证树的高度不会过高。

  2. 红色性质(Red Property):保证没有两个相邻的红色节点,也就是说,红色节点不能出现相连的情况。

这两个性质的作用如下:

  • 黑色平衡性 确保了红黑树的高度不会过高,使得其插入、删除等操作的时间复杂度保持在 O(log n) 级别。
  • 红色性质 则是为了防止出现连续的红色节点,这样可以避免了路径上出现两个相邻的红色节点,保证了在插入、删除等操作后的平衡性。

综合这两个性质,红黑树能够保持平衡,并且在动态插入、删除节点时,通过旋转和重新着色等操作来维护这些性质,从而保持树的平衡性。

相关推荐
阿弱1 分钟前
graph-core 策略合并机制的设计与用法
后端·agent
fatcoder6 分钟前
玩转Docker 08 — 实战:容器化真实后端并编排
后端·docker·容器
神奇小汤圆9 分钟前
从原始的CRUD 到高并发架构:关于秒杀系统的问题拆解与推演
后端
魔兽大山哥16 分钟前
NL2SQL 最怕越权:我在执行层叠了好几层 SQL 闸门
后端
foggyprojects33 分钟前
当 AI 输出销售额时,如何让它解释这个数字是怎么算出来的?
后端
一只叫煤球的猫1 小时前
开个新坑,从头开始完整拆解 Spring AI 2.0 的源码
后端·面试·aigc
GeekZHR1 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
.道阻且长.1 小时前
8.LeetCode算法习题讲解--滑动窗口--长度最小的子数组
算法·leetcode·职场和发展
(❁´◡`❁)Jimmy(❁´◡`❁)1 小时前
P1156 [USACO01OPEN] 垃圾陷阱
算法·动态规划
自进化Agent智能体1 小时前
Hermes Cron 定时任务 —— 让 Agent 自动工作
后端