11.1红黑树的复习

红黑树是一种自平衡的二叉查找树,它在保持查找效率的同时,通过特定的着色规则和旋转操作解决了二叉查找树在最坏情况下的效率问题。当数据按顺序插入导致退化成链表时,红黑树通过颜色调整和旋转确保树的平衡,从而保证查找、插入和删除操作的时间复杂度稳定在O(log n)。博客中展示了红黑树的节点定义、颜色属性、旋转操作以及插入操作的实现细节。
摘要由CSDN通过智能技术生成

红黑树产生的原因:

之前我们学习过二叉查找树,大部分情况下,发现它的查询效率比单纯的链表和数组的查询效率要高很多(二叉查找树相当于二分查找)
但如果按顺序插入的9,8,7,6,5,4,3,2,1 会发现二叉树变成链表样子,如果我们要查找1这个元素,查找的效率依旧会很低
效率低的原因在于这个树并不平衡,为了保证查找树的平衡性,引入2-3路平衡查找树与红黑树
但2-3查找树实现起来比较复杂,而且添加数据或删除数据的时候,为维护平衡问题可能会使得效率降低,所以引入红黑树
红黑树主要是对2-3树进行编码,红黑树背后的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信
息(替换3-结点)来表示2-3树。我们将树中的链接分为两种类型:
红链接:将两个2-结点连接起来构成一个3-结点; 黑链接:则是2-3树中的普通链接。
确切的说,我们将3-结点表示为由由一条左斜的红色链接(两个2-结点其中之一是另一个的左子结点)相连的两个2-
结点。这种表示法的一个优点是,我们无需修改就可以直接使用标准的二叉查找树的get方法

 

一般情况下,我们用结点为红色 代表父结点指向自己的线为红色

//红黑树
public class RedBlackTree<Key extends Comparable<Key>, Value> {
    //根节点
    private Node root;
    //记录树中元素的个数
    private int N;
    //红色链接
    private static final boolean RED = true;
    //黑色链接
    private static final boolean BLACK = false;

    /**
     * 判断当前节点的父指向链接是否为红色
     *
     * @param x
     * @return
     */
    private boolean isRed(Node x) {
        //空结点默认是黑色链接
        if (x == null) {
            return false;
        }
        //非空结点需要判断结点color属性的值
        return x.color == RED;
    }

    /**
     * 左旋转
     *
     * @param h
     * @return
     */
    private Node rotateLeft(Node h) {
        //找出当前结点h的右子结点
        Node hRight = h.right;
        //让当前结点h的右子结点的左子结点成为当前结点的右子结点
        h.right = hRight.left;
        //让当前结点h称为右子结点的左子结点
        hRight.left = h;
        //让当前结点h的color编程右子结点的color
        hRight.color = h.color;
        //让当前结点h的color变为RED
        h.color = RED;
        //返回当前结点的右子结点
        return hRight;
    }

    /**
     * 右旋转
     *
     * @param h
     * @return
     */
    private Node rotateRight(Node h) {
        //找出当前结点h的左子结点
        Node HLeft = h.left;
        //让当前结点h的左子结点的右子结点称为当前结点的左子结点
        h.left = HLeft.right;
        //让当前结点称为左子结点的右子结点
        HLeft.right = h;
        //让当前结点h的color值称为左子结点的color值
        HLeft.color = h.color;
        //让当前结点h的color变为RED
        h.color = RED;
        //返回当前结点的左子结点
        return HLeft;
    }

    /**
     * 颜色反转
     *
     * @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(Key key, Value value) {
        //在root整个树上插入key-val
        root = put(root, key, value);
        root.color = BLACK;
    }

    /**
     * 在指定树中,完成插入操作,并返回添加元素后新的树
     *
     * @param h
     * @param key
     * @param value
     * @return
     */
    private Node put(Node h, Key key, Value value) {
        if (h == null) {
            //标准的插入操作,和父结点用红链接相连
            N++;
            return new Node(key, value, null, null, RED);
        }
        //比较要插入的键和当前结点的键
        int cmp = key.compareTo((Key) h.key);
        if (cmp < 0) {
            //继续寻找左子树插入
            h.left = put(h.left, key, value);
        } else if (cmp > 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,从树中找出对应的值
    public Value get(Key key) {
        return get(root, key);
    }

    //从指定的树x中,查找key对应的值
    public Value get(Node x, Key key) {
        //如果当前结点为空,则没有找到,返回null
        if (x == null) {
            return null;
        }
        //比较当前结点的键和key
        int cmp = key.compareTo((Key) x.key);
        if (cmp < 0) {
            //如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
            return (Value) get(x.left, key);

        } else if (cmp > 0) {
            return (Value) get(x.right, key);
        } else {
            return (Value) x.value;
        }
    }

    //获取树中元素的个数
    public int size() {
        return N;
    }


    private class Node<Key, Value> {
        //存储键
        public Key key;
        //存储值
        private Value value;
        //记录左子结点
        public Node left;
        //记录右子结点
        public Node right;
        //由其父结点指向它的链接的颜色
        public 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 static void main(String[] args) {
        RedBlackTree<Integer, String> bt = new RedBlackTree<>();
        bt.put(4, "二哈");
        bt.put(1, "张三");
        bt.put(3, "李四");
        bt.put(5, "王五");
        System.out.println(bt.size());
        bt.put(1, "老三");
        System.out.println(bt.get(1));
        System.out.println(bt.size());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值