红黑树

本文深入解析红黑树的特性、时间复杂度、与二叉搜索树的区别、颜色变化规则及旋转操作,探讨红黑树在实际应用中的优势与局限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

红黑树作为一种面试常被提问的数据结构,自己应该弄懂其原理和树结构的变化规则,给自己不留坑。

问题:

  1. 红黑树的特性
  2. 红黑树的时间复杂度
  3. 红黑树与二叉搜索树的区别
  4. 红黑树的颜色变化规则
  5. 红黑树的左旋和右旋操作及其左旋和右旋时机
  6. 红黑树目前的应用场景有哪些?性能优缺点
  7. 红黑树的节点默认设置为红色和默认设置为黑色区别?TreeMap的默认设置颜色?

红黑树的特性

1.红黑树的所有节点不是红色就是黑色
2.红黑树的所有叶子节点为黑色(叶子节点指NULL节点)
3.根节点为黑色
4.任意一个红节点的两个子节点不能为红色
5.从任意一个节点出发,到齐叶子节点的所有路径上包含的黑色节点数相同

左旋和右旋操作过程

(图片来源于网络)
在这里插入图片描述
在这里插入图片描述

本人理解红黑树结构就是从这两张动图开始的

红黑树的代码实现网上很多,但是建议参照TreeMap的源码,亲自动手实现,找出差异。

代码实现

public class RebBlackTree<T extends Comparable<T>> {
	//根节点
    private Node<T> root;
    private static final boolean RED = false;
    private static final boolean BLACK = true;

    static class Node<T extends Comparable<T>> {
        boolean color;
        T key;
        Node<T> left;
        Node<T> right;
        Node<T> par
            ent;

        Node(T key) {
            this.key = key;
            //默认节点为红色
            this.color = RED;
            //父节点在插入的过程中会进行重新设置
            this.parent = null;
            this.left = null;
            this.right = null;
        }
    }


    /**
     * 红黑树的插入与二叉查找树的插入相同,在元素插入之后需要设置元素的父节点
     * 节点默认为红色
     * 插入之后进行红黑树的修正
     * 然后进行左旋和右旋操作
     * @param node
     */
    public void insert(Node<T> node) {
        int cmp;
        Node<T> x = this.root;
        Node<T> y = null;//父节点
        //寻找插入的父节点
        while (x != null) {
            y = x;
            cmp = node.key.compareTo(x.key);
            if (cmp < 0) {
                x = x.left;
            } else {
                x = x.right;
            }
        }
        //设置插入的父节点
        node.parent = y;
        /**
         * 判断插入到父节点的左边还是右边
         */
        if (y != null) {
            cmp = node.key.compareTo(y.key);
            if (cmp < 0) {
                y.left = node;
            } else {
                y.right = node;
            }
        } else {
            //如果父节点为空则此节点为根节点
            this.root = node;
        }
        /*
        从新插入的节点开始修正红黑树
      	此处应该可以理解红黑树的是局部平衡
        */
        insertFixUp(node);
    }

    /**
     * 插入节点之后修正红黑树
     * 修正红黑树的过程为从下往上
     * 修正的过程中轨进行颜色变化和左旋以及右旋操作
     * @param node
     */
    private void insertFixUp(Node node) {
        //父节点
        Node<T> parent;
        //爷爷节点
        Node<T> gparent;
        /**
         * 如果父节点不为空,并且父节点为红色
         */
        while (((parent = parentof(node)) != null) && isRed(parentof(node))) {
            gparent = parentof(parent);
            //如果父节点为左孩子
            if (parent == gparent.left) {
                Node<T> uncle = gparent.right;
                //case1:父节点为红色,叔叔的节点为红色
                if ((uncle != null) && isRed(uncle)) {
                    /**
                     * 设置爷爷节点为红色
                     * 设置父节点和叔叔节点为黑色
                     * 当前节点到爷爷节点
                     */
                    setRed(gparent);
                    setBlack(parent);
                    setBlack(uncle);
                    node = gparent;
                    continue;
                }
                //case2:当前节点为右孩子节点,父节点为红色,叔叔节点为黑色
                if (node == parent.right) {
                    /**
                     * 左旋操作
                     */
                    node = parent;
                    leftRotate(parent);
                }
                //case3:当前节点为左孩子节点,父节点为红色,叔叔节点为黑色,

                /**
                 * 右旋操作
                 * 父节点变为黑色,爷爷节点变为红色
                 */
                rightRotate(gparent);
                setRed(gparent);
                setBlack(parent);
            }
            //如果父节点为右孩子
            else {
                Node<T> uncle = gparent.left;
                //case1:当前节点为红色,叔叔节点为红色
                if ((uncle != null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }
                // Case 2条件:叔叔是黑色,且当前节点是左孩子
                if (parent.left == node) {
                    /**
                     * 右旋
                     */
                    node = parent;
                    rightRotate(parent);
                }
                // Case 3条件:叔叔是黑色,且当前节点是右孩子。
                /**
                 * 左旋
                 */
                setBlack(parent);
                setRed(gparent);
                leftRotate(gparent);
            }
        }
        /**
         * 如果父节点为空则此节点为根节点
         * 设置为黑色
         */
        setBlack(root);
    }
    
    /**
     * 以node节点进行左旋操作
     *
     * @param node
     */
    private void leftRotate(Node<T> node) {

        //x节点的右节点
        Node<T> x = node.right;
        //x的右节点为y节点的左节点
        node.right = x.left;
        node.parent = x;
        x.left.parent = node;
        x.parent = node.parent;
        x.left = node;

        //设置y节点的父节点
        if (node.parent != null) {
            if (node == node.parent.right) {
                node.parent.right = x;
            } else {
                node.parent.left = x;
            }
        } else {
            this.root = x;
        }
    }

    /**
     * 以node节点进行右旋操作
     * @param node
     */
    private void rightRotate(Node<T> node) {
        Node<T> x = node.left;
        x.parent = node.parent;
        x.right.parent = node;
        node.left = x.right;
        node.parent = x;

        if (node.parent != null) {
            //如果y节点本来为右节点
            //则父节点的右节点为x
            if (node == node.parent.right) {
                node.parent.right = x;
            } else {
                node.parent.left = x;
            }
        } else {
            this.root = x;
        }
    }

	//返回父节点
    private Node<T> parentof(Node<T> node) {
        return node != null ? node.parent : null;
    }

    //是否为红色节点
    private boolean isRed(Node<T> node) {
        return (node != null && node.color == RED) ? true : false;
    }

    //是否为黑色节点
    private boolean isBlack(Node<T> node) {
        return !isRed(node);
    }

    //设置黑色
    private void setBlack(Node<T> node) {
        if (node != null) {
            node.color = BLACK;
        }
    }
    
    //设置红色
    private void setRed(Node<T> node) {
        if (node != null) {
            node.color = RED;
        }
    }
}

规律

  • 当前节点为左节点,必为右旋;当前节点为右节点必为左旋
  • 左左(父节点和当前节点都为左节点),直接右旋;右右(父节点和当前节点都为右节点),直接左旋;旋转点为爷爷节点
  • 最后一次左旋或者右旋后,设置父亲节点为黑色,爷爷节点为红色
  • 左右操作和右左操作中,当前节点移动到父节点

总结

  1. 红黑树颜色默认为红色

    如果颜色默认设置为黑色,容易破坏性质5,则每次插入元素之后都要进行左旋或者右旋操作,影响性能

  2. 应用场景

    HashMap中链表会进行树化,树化时为红黑树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值