【数据结构-红黑树】

文章目录

红黑树

红黑树介绍

红黑树(Red-Black Tree)是一种自平衡的二叉查找树(Binary Search Tree, BST),它在普通二叉查找树的基础上增加了一些额外的约束条件,以确保树的平衡性,从而保证在最坏情况下插入、删除和查找操作的时间复杂度为 O(logn)。

红黑树的五个基本性质

红黑树是一种特殊的二叉查找树,它满足以下五个基本性质:

1.节点是红色或黑色,每个节点都有一个颜色属性,红色或黑色。

2.根节点必须是黑色

3.叶子节点(即空节点或 null)是黑色。

4.如果一个节点是红色,则它的两个子节点都是黑色。换句话说,红色节点不能连续出现。

5.从任意节点到其每个叶子节点的所有路径上,黑色节点的数量相同。这一性质确保了树的平衡性。

红黑树的平衡原理

红黑树通过上述性质来保证树的平衡。虽然红黑树不是完全平衡的二叉树,但它能够保证最长路径和最短路径的长度不会相差太大。具体来说,红黑树的最长路径不会超过最短路径的两倍,从而保证了树的近似平衡。

红黑树的操作

红黑树的主要操作包括插入、删除和查找。这些操作在普通二叉查找树的基础上增加了颜色调整和旋转操作,以确保树的平衡。

红黑树的操作

红黑树的主要操作包括插入、删除和查找。这些操作在普通二叉查找树的基础上增加了颜色调整和旋转操作,以确保树的平衡。
插入操作

插入新节点:将新节点插入到树中,新节点默认为红色。

修复树的性质:插入后可能违反红黑树的性质,需要通过以下操作修复:

颜色翻转:改变节点的颜色。

旋转操作:包括左旋和右旋,调整树的结构。
删除操作

删除节点:删除目标节点。

修复树的性质:删除后可能违反红黑树的性质,需要通过以下操作修复:

颜色调整:改变节点的颜色。

旋转操作:调整树的结构。
查找操作

查找操作与普通二叉查找树相同,从根节点开始,根据键值的大小关系逐层向下查找,直到找到目标节点或到达叶子节点。

代码实现

节点实现

java 复制代码
class Node<K extends Comparable<K>, V> {
    K key;
    V value;
    Node<K, V> left, right, parent;
    boolean color; // true 表示红色,false 表示黑色

    public Node(K key, V value) {
        this.key = key;
        this.value = value;
        this.color = true; // 新节点默认为红色
    }
}

插入和查询操作

java 复制代码
public class RedBlackTree<K extends Comparable<K>, V> {
    private Node<K, V> root;

    // 插入操作
    public void insert(K key, V value) {
        root = insert(root, key, value);
        root.color = false; // 根节点必须是黑色
    }

    private Node<K, V> insert(Node<K, V> node, K key, V value) {
        if (node == null) {
            return new Node<>(key, value);
        }

        if (key.compareTo(node.key) < 0) {
            node.left = insert(node.left, key, value);
            node.left.parent = node;
        } else if (key.compareTo(node.key) > 0) {
            node.right = insert(node.right, key, value);
            node.right.parent = node;
        } else {
            node.value = value; // 如果键已存在,更新值
        }

        // 修复红黑树性质
        return fixAfterInsertion(node);
    }

    // 修复插入后的红黑树性质
    private Node<K, V> fixAfterInsertion(Node<K, V> node) {
        while (node != null && node != root && node.parent.color) {
            if (node.parent == node.parent.parent.left) {
                Node<K, V> uncle = node.parent.parent.right;
                if (uncle != null && uncle.color) {
                    // 情况1:叔叔节点是红色
                    node.parent.color = false;
                    uncle.color = false;
                    node.parent.parent.color = true;
                    node = node.parent.parent;
                } else {
                    if (node == node.parent.right) {
                        // 情况2:右倾,先左旋
                        node = node.parent;
                        rotateLeft(node);
                    }
                    // 情况3:左倾,右旋
                    node.parent.color = false;
                    node.parent.parent.color = true;
                    rotateRight(node.parent.parent);
                }
            } else {
                Node<K, V> uncle = node.parent.parent.left;
                if (uncle != null && uncle.color) {
                    // 情况1:叔叔节点是红色
                    node.parent.color = false;
                    uncle.color = false;
                    node.parent.parent.color = true;
                    node = node.parent.parent;
                } else {
                    if (node == node.parent.left) {
                        // 情况2:左倾,先右旋
                        node = node.parent;
                        rotateRight(node);
                    }
                    // 情况3:右倾,左旋
                    node.parent.color = false;
                    node.parent.parent.color = true;
                    rotateLeft(node.parent.parent);
                }
            }
        }
        return node;
    }

    // 左旋操作
    private void rotateLeft(Node<K, V> x) {
        Node<K, V> 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 rotateRight(Node<K, V> x) {
        Node<K, V> y = x.left;
        x.left = y.right;
        if (y.right != null) {
            y.right.parent = x;
        }
        y.parent = x.parent;
        if (x.parent == null) {
            root = y;
        } else if (x == x.parent.right) {
            x.parent.right = y;
        } else {
            x.parent.left = y;
        }
        y.right = x;
        x.parent = y;
    }

    // 查找操作
    public V get(K key) {
        Node<K, V> node = root;
        while (node != null) {
            int cmp = key.compareTo(node.key);
            if (cmp < 0) {
                node = node.left;
            } else if (cmp > 0) {
                node = node.right;
            } else {
                return node.value;
            }
        }
        return null;
    }
}

代码说明

节点定义:

每个节点包含键、值、左右子节点和父节点指针,以及一个颜色属性(红色或黑色)。

插入操作:

插入新节点时,新节点默认为红色。

插入后调用 fixAfterInsertion 方法修复红黑树的性质。

修复逻辑:

根据红黑树的性质,修复插入操作可能破坏的平衡。

主要处理以下几种情况:

叔叔节点是红色:将父节点和叔叔节点改为黑色,祖父节点改为红色,继续向上检查。

叔叔节点是黑色:根据节点的位置进行旋转操作,调整树的结构。

旋转操作:

左旋:将右子节点提升为新的根节点,调整子树的连接关系。

右旋:将左子节点提升为新的根节点,调整子树的连接关系。

查找操作:

从根节点开始,根据键值的大小关系逐层向下查找,直到找到目标节点或到达叶子节点。

相关推荐
₍˄·͈༝·͈˄*₎◞ ̑̑码1 小时前
数据结构:顺序表
c语言·数据结构
因兹菜6 小时前
[LeetCode]day21 15.三数之和
数据结构·算法·leetcode
萌の鱼8 小时前
leetcode 2466. 统计构造好字符串的方案数
数据结构·c++·算法·leetcode
姓学名生10 小时前
408-数据结构
数据结构
Mercury_Lc14 小时前
【力扣 - 简单题】88. 合并两个有序数组
数据结构·算法·leetcode·acm
冠位观测者14 小时前
【Leetcode 每日一题】1760. 袋子里最少数目的球
数据结构·算法·leetcode
apcipot_rain16 小时前
数据结构实验——排序算法的实现与分析
c语言·数据结构·算法·排序算法
萌の鱼16 小时前
leetcode 2684. 矩阵中移动的最大次数
数据结构·c++·算法·leetcode·矩阵
小猪咪piggy16 小时前
【数据结构】(8) 二叉树
数据结构