算法与数据结构(八):树之红黑树

  • 博主会对算法与数据结构会不断进行更新,敬请期待,如有什么建议,欢迎联系。
  • 二叉树中最特殊的红黑树可以算是其中一个,我们知道jdk8中对HashMap进行了优化,底层数据结构采用了数组+链表+红黑树,当链表达到8时,或数组达到64时就会转成红黑树。红黑树可以说是2-3查找树的升级,红黑树也是一种平衡树,平衡树解决了如果插入的数据为有序元素,例如:1,2,3,4,5,6则二叉树就会变成链表的问题。

红黑树的定义:

红黑树是含有红黑链接,并满足下列条件的二叉平衡树:

  • 红链接均为左连接;
  • 没有任何一个节点同时和两个红链接相连;
  • 该树是完美黑色平衡树,即任意空链接到到根节点的路径黑链接数量相等;

下面是红黑树和2-3树的对应关系:

在这里插入图片描述

红黑树节点设计:

在这里插入图片描述

红黑树的实现细节:

  • 红黑树是一种二叉平衡树;
  • 当某个节点的左子节点为黑色,右子节点为红色,此时需要左旋;
  • 当某个节点左子节点为红色,左子节点的左子节点也为红色,此时需要右旋;
  • 当一个节点的左子节点为红色,右子节点也为红色,此时需要颜色反转,把左子节点和右子节点的颜色变为黑色,同时让当前节点的颜色变为红色。

红黑树的左旋:

  • 让x的左子节点变为h的右子节点,h.right = x.left;
  • 让h成为x的左子节点,x.left = h;
  • 让h的color属性变为x的color属性,x.color = h.color;
  • 让h的color属性变为red,h.color = RDE(true);

在这里插入图片描述

红黑树的右旋:

  • 让x的右子节点变为h的左子节点,h.left = x.right;
  • 让h成为x的右子节点,x.right = h;
  • 让h的color变为x的color, x.color = h.color;
  • 让h的color为红色,h.color = RED(true);

在这里插入图片描述

  • 红黑树的实现代码如下:

package com.victor.tree;

import java.util.Objects;

/**
 * 红黑树
 *
 * @description: 红黑树的实现
 * @author: victor
 */
public class RedBlackTree<K extends Comparable<K>, V> {
    /*指向本节点的为红色*/
    private static final boolean RED = true;
    /*指向本节点的为黑色*/
    private static final boolean BLACK = false;
    /*根节点*/
    private Node root;
    /*节点的个数*/
    private int N;

    /**
     * 内部类Node节点
     */
    private class Node {
        final K key;
        V value;
        Node left;
        Node right;
        boolean color;   //指向本节点的的颜色

        public Node(K key, V value, Node left, Node right, boolean color) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
            this.color = color;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        @Override
        public String toString() {
            return key + "=" + value;
        }

        public final int hashCode() {
            return Objects.hash(key, value);
        }

        @Override
        @SuppressWarnings("unchecked")
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Node node = (Node) o;
            return Objects.equals(key, node.key) && Objects.equals(value, node.value);
        }

        /**
         * 设置新值,并返回旧值
         *
         * @param newValue 设置的新值
         * @return 返回旧值
         */
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    }

    private boolean isRed(Node node) {
        //空节点默认是黑色连接
        if (node == null) return BLACK;
        return node.color == RED;
    }

    /**
     * 树节点的数量
     *
     * @return 树的尺寸
     */
    public int size() {
        return N;
    }

    /**
     * 左旋调整
     *
     * @param h 左旋的节点
     * @return 返回的节点
     */
    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color; //x的颜色变为h的颜色,这个颜色不确定
        h.color = RED;
        return x;
    }

    /**
     * 右旋调整
     *
     * @param h 右旋的节点
     * @return 返回的节点
     */
    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        return x;
    }

    /**
     * 颜色反转
     *
     * @param h 反转的节点
     */
    private void flipColors(Node h) {
        h.left.color = BLACK;
        h.right.color = BLACK;
        h.color = RED;
    }

    /**
     * 在整个树上完成插入操作
     *
     * @param key   键
     * @param value 值
     */
    public void put(K key, V value) {
        root = put(root, key, value);
        root.color = BLACK;
    }

    /**
     * 在指定树中完成插入操作,返回插入后的新树
     *
     * @param h     h树
     * @param key   键
     * @param value 值
     * @return 插入后的新树
     */
    private Node put(Node h, K key, V value) {
        if (h == null) return new Node(key, value, null, null, RED);
        int result = key.compareTo(h.key);
        if (result < 0) h.left = put(h.left, key, value);
        else if (result > 0) h.right = put(h.right, key, value);
        else 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查值
     *
     * @param key 键
     * @return 值
     */
    public V get(K key) {
        return get(root, key);
    }

    /**
     * 从指定树中,根据key查找值
     *
     * @param h   h树
     * @param key 键
     * @return 值
     */
    private V get(Node h, K key) {
        if (h == null) return null;
        int result = key.compareTo(h.key);
        if (result < 0) return get(h.left, key);
        else if (result > 0) return get(h.right, key);
        else return h.value;
    }

}

注意事项:

  • 颜色反转要在左旋和右旋之后进行,因为右旋之后就会导致该节点的左子节点与右子节点为红色,此时要进行颜色反转。
  • 根节点每次在插入元素之后都要让根节点的颜色变为黑色,因为树在旋转的时候有可能会导致根节点的颜色为红色。
  • 在左旋或右旋时,x.color不一定为黑色,有可能为红色,因此 x.color = h.color; x的颜色变为h的颜色,这个颜色不确定。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值