红黑树

 视频链接:红黑树原理源码讲解(java),全B站讲解最细致版本,看完月薪最少涨5k!_哔哩哔哩_bilibili

github红黑树源码:Your Repositories (github.com)

一、树结构入门

  1.1 二叉查找树

  1.2 AVL树

二、红黑树原理讲解

  2.1 红黑树性质

 

 

   2.2 自平衡

    

 

 

     

 

 

  2.3 红黑树查找

    和AVL树的查找相同

 

 

   2.4 红黑树插入

      前提:插入节点必须为红色

      情景1:红黑树为空树

        直接插入,并将插入节点变为黑色

      情景2:插入节点的key已存在

        更新当前节点的值为插入节点的值

      情景3:插入节点的父节点为黑节点

        直接插入即可

      情景4:插入节点的父节点为红节点

        情景4.1 :叔叔节点存在并且为红色

              将叔叔节点和父节点改为黑色,将祖父节点改为红色,并将祖父节点设置为当前节点进行后续处理

        情景4.2:叔叔节点不存在或为黑色节点,并且父节点为祖父节点的左子节点

            情景4.2.1  新插入节点为其父节点的左子节点 (LL情况)

                   将父节点设置为黑色,并将祖父节点设置为红色,再将祖父节点右旋。

            情景4.2.2 新插入节点为其父节点的右子节点(LR情况)

                  将父节点左旋,变成LL情况,再根据LL情况处理

        情景4.3 :叔叔节点不存在或为黑色节点,并且父节点为祖父节点的右子节点

            和情景4.2处理类似

      详细看下图

    

 

 

     

 

 

     

 

 

     

 

 

     

 

 

 

 

 

     

 

 

     

  

 

 

   

 

 

 

     

三、手写红黑树

  1.   创建RBTree
  2.   辅助方法:parentOf(RBNode node)  isRed(RBNode node) setRed(RBNode node) setBlack(RBNode node) inOrderPrint()
  3.        左旋: leftRotate(node)
  4.   右旋: rightRotate(node)
  5.        公开插入接口: insert(K key, V value)
  6.   内部插入接口:insert(RBNode node)
  7.        平衡调整:insertFixUp(RBNode node)
package com.lili.rbtree;

public class RBTree<K extends Comparable<K>,V>{
    private static final boolean RED = true;
    private static final boolean BLACK = false;
    // 树根的引用
    private RBNode root;

    /**
     * 返回根节点
     */
    public RBNode getRoot(){
        return this.root;
    }

    /**
     * 1、获取当前节点的父节点
     * @param <K>
     * @param <V>
     */
    private RBNode parentOf(RBNode node){
        if(node != null){
            return node.parent;
        }
        return null;
    }

    /**
     * 2、节点是否为红色
     * @param node
     */
    private boolean isRed(RBNode node){
        if(node != null){
            return node.color == RED;
        }
        return false;
    }
    // 2.1 设置节点为红色
    private void setRed(RBNode node){
        if(node != null){
            node.color = RED;
        }
    }

    /**
     * 3、节点是否为黑色
     * @param node
     */
    private boolean isBlack(RBNode node){
        if(node != null){
            return node.color == BLACK;
        }
        return false;
    }
    // 3.1 设置节点为黑色
    private void setBlack(RBNode node){
        if(node != null){
            node.color = BLACK;
        }
    }

    /**
     * 4、中序打印二叉树
     */
    public void inOrderPrint(){
        inOrderPrint(this.root);
    }
    private void inOrderPrint(RBNode node){
        if(node!=null){
            inOrderPrint(node.left);
            System.out.println("key:"+node.key+",value:"+node.value);
            inOrderPrint(node.right);
        }
    }

    /**
     * 5、左旋 x节点
     *     p                  p
     *     |                  |
     *     x                  y
     *    / \      ---->     / \
     *   lx  y              x  ry
     *      / \            / \
     *     ly ry          lx ly
     *
     * 1.将x的右子节点指向y的左子节点 将y的左子节点的父节点更新为x
     * 2.当x的父节点不为空时,更新y的父节点为x的父节点 ,并将x的父节点指向y
     * 3.将x的父节点更新为y,将y的左子节点更新为x
     */
    private void leftRotate(RBNode x){
        RBNode y = x.right;
        // 1.将x的右子节点指向y的左子节点 将y的左子节点的父节点更新为x
        x.right = y.left;
        if(y.left != null){
            y.left.parent = x;
        }

        // 2.当x的父节点不为空时,更新y的父节点为x的父节点 ,并将x的父节点指向y
        if(x.parent != null){
            y.parent = x.parent;
            if(x == x.parent.left){
                x.parent.left = y;
            }else{
                x.parent.right = y;
            }
        }else{
            this.root = y;
            y.parent = null;
        }

        // 3.将x的父节点更新为y,将y的左子节点更新为x
        x.parent = y;
        y.left = x;
    }

    /**
     * 6、右旋 y节点
     *     p                  p
     *     |                  |
     *     y                  x
     *    / \      ---->     / \
     *   x  ry              lx  y
     *  / \                    / \
     * lx ly                  ly ry
     *
     * 1.将y的左子节点指向x的右子节点 将x的右子节点的父节点更新为y
     * 2.当y的父节点不为空时,更新x的父节点为y的父节点 ,并将y的父节点指向x
     * 3.将y的父节点更新为x,将x的右子节点更新为y
     */
    private void rightRotate(RBNode y){
        RBNode x = y.left;
        // 1.将y的左子节点指向x的右子节点 将x的右子节点的父节点更新为y
        y.left = x.right;
        if(x.right != null){
            x.right.parent = y;
        }

        // 2.当y的父节点不为空时,更新x的父节点为y的父节点 ,并将y的父节点指向x
        if(y.parent != null){
            x.parent = y.parent;
            if(y == y.parent.left){
                y.parent.left = x;
            }else{
                y.parent.right = x;
            }
        }else{
            this.root = x;
            x.parent = null;
        }

        // 3.将y的父节点更新为x,将x的右子节点更新为y
        y.parent = x;
        x.right = y;
    }

    /**
     * 公开的插入方法
     * @param key
     * @param value
     */
    public void insert(K key, V value){
        RBNode node = new RBNode();
        node.setKey(key);
        node.setValue(value);
        // 新节点一定是红色
        node.setColor(RED);
        insert(node);
    }
    private void insert(RBNode node){
        // 第一步 : 查找当前节点的父节点
        RBNode parent = null;
        RBNode x = this.root;

        while(x != null){
            parent = x;

            // cmp>0 说明node.key>x.key 需要到x的右子树查找
            // cmp==0 说明node.key==x.key 需要进行替换操作
            // cmp<0 说明node.key<x.key 需要到x的左子树查找
            int cmp = node.key.compareTo(x.key);

            if(cmp >0){
                x = x.right;
            }else if(cmp == 0){
                x.setValue(node.getValue());
                return;
            }else{
                x = x.left;
            }
        }

        node.parent = parent;

        if(parent != null){
            // 判断node与parent的key谁大
            int cmp = node.key.compareTo(parent.key);
            if(cmp>0){
                parent.right = node;
            }else{
                parent.left = node;
            }
        }

        // 需要调用红黑树的平衡方法
        insertFixUp(node);

    }

    /**
     * 平衡调整
     * 前提:插入节点必须为红色
     *
     * 情景1:红黑树为空树
     *     直接插入,并将插入节点变为黑色
     * 情景2:插入节点的key已存在
     *     更新当前节点的值为插入节点的值
     * 情景3:插入节点的父节点为黑节点
     *     直接插入即可
     * 情景4:插入节点的父节点为红节点
     *     情景4.1 :叔叔节点存在并且为红色
     *          将叔叔节点和父节点改为黑色,将祖父节点改为红色,并将祖父节点设置为当前节点进行后续处理
     *     情景4.2:叔叔节点不存在或为黑色节点,并且父节点为祖父节点的左子节点
     *         情景4.2.1  新插入节点为其父节点的左子节点 (LL情况)
     *               将父节点设置为黑色,并将祖父节点设置为红色,再将祖父节点右旋。
     *         情景4.2.2 新插入节点为其父节点的右子节点(LR情况)
     *               将父节点左旋,变成LL情况,再根据LL情况处理
     *     情景4.3 :叔叔节点不存在或为黑色节点,并且父节点为祖父节点的右子节点
     *          和情景4.2处理类似
     * @param node
     */
    private void insertFixUp(RBNode node){
        this.root.setColor(BLACK);

        RBNode parent = parentOf(node);
        RBNode gparent = parentOf(parent);

        // 1.如果父节点是红色(父节点为红色则一定存在祖父节点)
        if(parent != null && isRed(parent)){
            RBNode uncle; // 叔叔节点

            // 父节点再祖父节点左边
            if(parent == gparent.left){
                uncle = gparent.right;
                //情景4.1 :叔叔节点存在并且为红色(即叔叔节点和父节点都为红色)
                // 将叔叔节点和父节点改为黑色,将祖父节点改为红色,并将祖父节点设置为当前节点进行后续处理
                if(uncle != null && isRed(uncle)){
                    parent.setColor(BLACK);
                    uncle.setColor(BLACK);
                    gparent.setColor(RED);
                    insertFixUp(gparent);
                    return;
                }else{ // 情景4.2:叔叔节点不存在或为黑色节点,并且父节点为祖父节点的左子节点
                    // 新插入节点为其父节点的左子节点 (LL情况)
                    if(node == parent.left){
                        parent.setColor(BLACK);
                        gparent.setColor(RED);
                        rightRotate(gparent);
                        return;
                    }else { // 新插入节点为其父节点的右子节点(LR情况)
                        leftRotate(parent);
                        insertFixUp(parent);
                        return;
                    }
                }
            }else{ // 父节点再祖父节点右边
                uncle = gparent.left;
                //情景4.1 :叔叔节点存在并且为红色(即叔叔节点和父节点都为红色)
                // 将叔叔节点和父节点改为黑色,将祖父节点改为红色,并将祖父节点设置为当前节点进行后续处理
                if(uncle != null && isRed(uncle)){
                    parent.setColor(BLACK);
                    gparent.setColor(RED);
                    uncle.setColor(BLACK);
                    insertFixUp(gparent);
                    return;
                }else{
                    // 新插入节点为其父节点的左子节点 (LL情况)
                    if(node == parent.right){
                        parent.setColor(BLACK);
                        gparent.setColor(RED);
                        leftRotate(gparent);
                    }else{
                        rightRotate(parent);
                        node.setColor(BLACK);
                        gparent.setColor(RED);
                        leftRotate(gparent);
                    }
                }
            }

        }
        // 2. 如果父节点是黑色 直接插入即可

        // 3. 如果没有父节点
        if(parent == null){
            node.color = BLACK;
            this.root = node;
        }
    }


    static class RBNode<K extends Comparable<K>,V>{
        private RBNode parent;
        private RBNode left;
        private RBNode right;
        private boolean color;
        private K key;
        private V value;

        public RBNode() {
        }

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

        public void setParent(RBNode parent) {
            this.parent = parent;
        }

        public void setLeft(RBNode left) {
            this.left = left;
        }

        public void setRight(RBNode right) {
            this.right = right;
        }

        public void setColor(boolean color) {
            this.color = color;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public void setValue(V value) {
            this.value = value;
        }

        public RBNode getParent() {
            return parent;
        }

        public RBNode getLeft() {
            return left;
        }

        public RBNode getRight() {
            return right;
        }

        public boolean isColor() {
            return color;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }
    }
}

 

posted @ 2021-10-28 09:52  微~粒  阅读(45)  评论(0)    收藏  举报