红黑树的底层结构实现

红黑树的定义

红黑树是含有红黑链接并满足以下条件的二叉查找树

1、红链接均为左连接;

2、没有任何一个结点同时和两条红链接相连;

3、该树是完美黑色平衡的,即任意空链接到根结点的路径上的黑链接数量相同。

 红黑树中存储元素的结点

因为每个结点都只会有一条指向自己的链接(即它的父结点指向它),我们可以在之前的Node结点中新增一个布尔类型的变量color来表示此链接的颜色。我们规定:如果指向此结点的链接是红色的,那么该变量的值为true;如果指向此结点的链接时黑色的,那么该变量的值为false。例如下图所示,c是当前结点,h指的是它的父结点,所以h的左子结点(即c结点)的color的值是true

 结点API设计:

 代码实现:

/**
     * 结点类
     */
    private class Node {
        /**
         * 存储键
         */
        private Key key;
        //存储值
        private Value value;
        //记录左子结点
        private Node left;
        //记录右子结点
        private Node right;
        //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
        private boolean color;

        /**
         * 构造方法
         */
        public Node(Key key, Value value, Node left, Node right, boolean color) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
            this.color = color;
        }

    }

平衡化

在对红黑树进行一些增删改的操作后,很有可能会出现红色的右链接或者两条连续的红色的链接,而这些都不满足红黑树的定义,所以我们需要对这些情况通过旋转进行修复,让红黑树保持平衡。

左旋

当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。

例如:当前结点为h,它的右子结点为x;

左旋过程:

1、让x的左子结点变为h的右子结点:h.right=x.left;

2、让h成为x的左子结点:x.left=h;

3、让h的color属性变为x的color属性值:x.color=h.color;

4、让h的color属性变为RED:h.color=true;

右旋

当某个结点的左子结点为红色,左子结点的左子结点也为红色,此时需要右旋。

例如:当前结点为h,它的左子结点为x;

右旋过程:

1、让x的右子结点变为h的左子结点:h.left=x.right;

2、让h成为x的右子结点:x.right=h;

3、让h的color属性变为x的color属性值:x.color=h.color;

4、让h的color属性变为RED:h.color=true;

颜色反转

当一个结点的左子结点和右子结点的color都为RED时,此时只需要把左子结点和右子结点的颜色变为BLACK,同时让当前结点的颜色变为RED即可。

根结点的颜色总是黑色

在结点Node对象中color属性表示的是父结点指向当前结点的连接的颜色,由于根结点不存在父结点,所以每次插入操作后,我们都需要把根结点的颜色设置为黑色。

红黑树的API设计

 红黑树代码实现

/**
 * @author: xuzhilei
 * @create: 2021-12-21
 * @description: 红黑树
 **/
public class RedBlackTree<Key extends Comparable<Key>, Value> {
    /**
     * 根结点
     */
    private Node root;
    /**
     * 记录树中的元素个数
     */
    private int number;
    /**
     * 表示红色链接
     */
    private static final boolean RED = true;
    /**
     * 表示黑色链接
     */
    private static final boolean BLACK = false;

    /**
     * 结点类
     */
    private class Node {
        /**
         * 存储键
         */
        private Key key;
        //存储值
        private Value value;
        //记录左子结点
        private Node left;
        //记录右子结点
        private Node right;
        //标记父结点指向当前结点的链接是否是红色:true表示是红色,false表示不是红色(即黑色)
        private boolean color;

        /**
         * 构造方法
         */
        public Node(Key key, Value value, Node left, Node right, boolean color) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
            this.color = color;
        }

    }

    /**
     *构造方法
     */
    public RedBlackTree() {
        this.root = null;
        this.number = 0;
    }

    /**
     * 获取红黑树中的元素个数
     *
     * @return int
     */
    public int size() {
        return number;
    }

    /**
     * 判断当前结点x的父结点指向它的链接是否为红色
     *
     * @param x 当前结点
     * @return boolean
     */
    private boolean isRed(Node x) {
        if (x == null) {
            return false;
        }
        return x.color == RED;
    }

    /**
     * 左旋转
     * 当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
     * 前提:当前结点为h,它的右子结点为x;
     * 左旋过程:
     * 1.让x的左子结点变为h的右子结点:h.right=x.left;
     * 2.让h成为x的左子结点:x.left=h;
     * 3.让h的color属性变为x的color属性值:x.color=h.color;
     * 4.让h的color属性变为RED:h.color=true;
     *
     * @param h 当前结点
     * @return h的右子结点x
     */
    private Node rotateLeft(Node h) {
        //获取h结点的右子结点x
        Node x = h.right;
        //找到x结点的左子结点,让x结点的左子结点成为h结点的右子结点
        h.right = x.left;
        //让h成为x的左子结点
        x.left = h;
        //让h的color属性变为x的color属性值
        x.color = h.color;
        //让h的color属性变为RED
        h.color = RED;
        //返回x结点
        return x;
    }

    /**
     * 右旋转
     * 当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
     * 前提:当前结点为h,它的左子结点为x;
     * 右旋过程:
     * 1. 让x的右子结点成为h的左子结点:h.left = x.right;
     * 2. 让h成为x的右子结点:x.right=h;
     * 3. 让x的color变为h的color属性值:x.color = h.color;
     * 4. 让h的color为RED;
     *
     * @param h 当前结点
     * @return h的左子结点x
     */
    private Node rotateRight(Node h) {
        //获取h结点的左子结点x
        Node x = h.left;
        //找到x结点的右子结点,让x结点的右子结点成为h结点的左子结点
        h.left = x.right;
        //让h成为x的右子结点
        x.right = h;
        //让x的color变为h的color属性值
        x.color = h.color;
        //让h的color属性值变为RED
        h.color = RED;
        //返回x结点
        return x;
    }

    /**
     * 颜色反转
     * 当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
     *
     * @param h 当前结点
     */
    private void flipColors(Node h) {
        //当前结点的color属性值变为RED
        h.color = RED;
        //左子结点和右子结点的color属性都变为BLACK
        h.left.color = BLACK;
        h.right.color = BLACK;
    }

    /**
     * 向整个树中插入键值对key-value
     *
     * @param key   待插入键
     * @param value 待插入值
     */
    public void put(Key key, Value value) {
        root = put(this.root, key, value);
        //根结点的color属性总是黑色
        root.color = BLACK;
    }

    /**
     * 往指定树h中插入键值对key-value,并返回插入元素后的新树
     *
     * @param h     指定树的根结点
     * @param key   待插入键
     * @param value 待插入值
     */
    private Node put(Node h, Key key, Value value) {
        //如果当前结点为空,则直接创建一个结点并返回此结点
        if (h == null) {
            //元素个数+1
            number++;
            return new Node(key, value, null, null, RED);
        }

        //比较结点h的键和参数key的大小
        int comp = key.compareTo(h.key);
        if (comp < 0) {
            //key比当前结点h的键要小,继续向h的左子结点所在的树进行插入操作
            h.left = put(h.left, key, value);
        } else if (comp > 0) {
            //key比当前结点h的键要大,继续向h的右子结点所在的树进行插入操作
            h.right = put(h.right, key, value);
        } else {
            //key与当前结点h的键相等,则用待插入的value替换当前结点的value值
            h.value = value;
        }

        //满足左旋转条件:当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
        if (isRed(h.right) && !isRed(h.left)) {
            h = rotateLeft(h);
        }

        //满足右旋转条件:当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
        if (isRed(h.left) && isRed(h.left.left)) {
            h = rotateRight(h);
        }

        //满足颜色反转:当某个结点的左子结点是红色,且右子结点也是红色,需要颜色反转
        if (isRed(h.left) && isRed(h.right)) {
            flipColors(h);
        }

        return h;
    }

    /**
     * 根据key在整个树中找出对应的value
     *
     * @param key 指定的key
     * @return Value 对应的value
     */
    public Value get(Key key) {
        return get(root, key);
    }

    /**
     * 根据key在指定树x中找出对应的value
     *
     * @param x   指定树的根结点
     * @param key 指定的key
     * @return Value 对应的value
     */
    private Value get(Node x, Key key) {
        if (x == null) {
            return null;
        }
        //比较当前结点x的键和参数key的大小
        int comp = key.compareTo(x.key);
        if (comp < 0) {
            //key比当前结点x的键要小,则继续向x的左子结点所在的树进行查找操作
            return get(x.left, key);
        } else if (comp > 0) {
            //key比当前结点x的键要大,则继续向x的右子结点所在的树进行查找操作
            return get(x.right, key);
        } else {
            //key与当前结点x的键相等,则返回结点x的value值
            return x.value;
        }
    }

    //测试
    public static void main(String[] args) {
        //创建红黑树
        RedBlackTree<String, String> redBlackTree = new RedBlackTree<>();

        //插入元素
        redBlackTree.put("1", "张三");
        redBlackTree.put("2", "李四");
        redBlackTree.put("3", "王五");

        //获取树中元素个数
        int size = redBlackTree.size();
        System.out.println(size);

        //从树中获取元素
        String s = redBlackTree.get("1");
        System.out.println(s);

    }


}

测试结果:

  以上是个人随手敲的demo,如有不正确的地方,可以在下方留言指正,谢谢。与各位CSDN的伙伴们共勉,每天记录一点点,每天进步一点点

### 回答1: 红黑树是一种平衡二叉树,它的每个节点都有一个颜色属性,可以是红色或者黑色,它的实现原理是:每个节点要么是红色,要么是黑色;根节点是黑色;每个叶子节点都是黑色;如果一个节点是红色,则它的子节点必须是黑色;从任一节点到其每个叶子的所有路径上的黑色节点数量必须相同。 ### 回答2: 红黑树是一种自平衡的二叉查找树,其底层实现原理包括以下几点: 1. 每个节点都有一个颜色属性,可以是红色或黑色。根节点和叶子节点(即空节点)都是黑色的。 2. 任意节点到其每个叶子节点的路径上含有相同数目的黑色节点,即保持了从根到叶子节点的最长路径的长度不会超过最短路径的两倍。 3. 不能有连续的红色节点,即红色节点的子节点必须是黑色的。这个规则保证了从根节点到叶子节点的每个路径上的黑色节点个数是相同的。 4. 当插入或删除节点时,需要通过旋转和颜色变换操作来保持红黑树的这些性质。 具体的插入操作如下: - 将新节点插入为红色节点,保持二叉查找树的性质。 - 如果新节点的父节点是黑色,那么什么都不需要做。 - 如果新节点的父节点是红色,那么需要通过旋转和颜色变换操作来平衡树。 - 如果新节点的叔叔节点也是红色,则将父节点和叔叔节点的颜色改为黑色,将祖父节点的颜色改为红色,再将祖父节点作为新节点进行操作。 - 如果新节点的叔叔节点是黑色或者不存在,需要进行旋转操作来保持红黑树的性质。 - 如果新节点的父节点是祖父节点的左子节点,叔叔节点是祖父节点的右子节点,那么进行左旋操作。 - 如果新节点的父节点是祖父节点的右子节点,叔叔节点是祖父节点的左子节点,那么进行右旋操作。 - 在旋转后,需要颜色变换来保持红黑树的性质。 删除操作也需要根据情况进行旋转和颜色变换来保持红黑树的性质。 通过这些操作,红黑树可以保持在插入和删除节点时,仍然保持二叉查找树的有序性,同时能够保持整棵树的平衡性,避免出现最坏情况下的树高度过高,从而提高了查找、插入和删除操作的效率。 ### 回答3: 红黑树是一种自平衡的二叉搜索树,它的底层实现原理主要包括以下几个方面: 1. 节点的结构红黑树的节点包含关键字、颜色、左孩子、右孩子和父节点等信息。其中,节点的颜色为红色或黑色,根节点颜色为黑色。 2. 插入操作:当向红黑树中插入一个新节点时,首先按照二叉搜索树的规则找到插入位置,并将节点的颜色设置为红色。然后根据红黑树的特性进行相应的调整,包括变色和旋转等操作。调整的目的是保持红黑树的平衡性和特性,确保没有连续的红色节点、每条路径上的黑色节点数量相等。 3. 删除操作:当从红黑树中删除一个节点时,首先按照二叉搜索树的规则找到要删除的节点,并进行删除。然后根据红黑树的特性进行相应的调整,包括变色和旋转等操作。调整的目的是保持红黑树的平衡性和特性,确保没有连续的红色节点、每条路径上的黑色节点数量相等。 4. 自平衡:红黑树中的插入和删除操作都会导致树的结构发生变化,可能破坏红黑树的平衡性和特性。为了保持红黑树的平衡,需要对树进行相应的调整。调整的方式包括变色和旋转等操作,以保持红黑树的特性。 总之,红黑树通过合理的节点结构和自平衡的调整操作,保持了树的平衡性和特性,使得插入、删除和查找等操作的时间复杂度能够保持在O(log n)的水平。这使得红黑树成为一种高效的数据结构,在许多应用场景中得到广泛应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值