红黑树平衡经典案例(图解)及插入Java实现(代码)

红黑树平衡的经典案例

红黑树平衡经典案例(图解)及插入Java实现(代码)_第1张图片

红黑树几条规则(查阅相关论文得到):

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。 红黑树的每个节点的属性除了有一个 key 和 3 个指针:parent、lchild、rchild 外 ,还有一个属性 :color:红或黑 。
与大多数二叉查找树一样 ,红黑树中较小的键值也是在左子树保存 。 红黑树除了具有二叉查找树所有性质外,还具有以下 5 点性质
①节点是红色或黑色。
②根是黑色。
③所有叶子都是黑色(叶子是 NUIL 节点)。
④每个红色节点的两个子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
⑤从任一个节点到其每个叶子的所有路径都包含相同数目的黑色节点。
引用自《红黑树关键算法研究》–马国富,张涵


  1. 上图中6为待插入的节点,此时破坏了红黑树的第四个规则,出现了两个连续的红色节点
    此时的情况为6的父节点(7)和叔叔节点都为红色,需要将爷爷(12)节点变为红色,叔叔节点(13)和父节点(7)变为黑色,如图
    红黑树平衡经典案例(图解)及插入Java实现(代码)_第2张图片

  2. 此时5和12 同为红色,不符合红黑树的定义,观察得叔叔(30)节点为爷爷节点的右节点,而12节点为父节点的右节点,此时需要进行对5进行左旋,如图
    红黑树平衡经典案例(图解)及插入Java实现(代码)_第3张图片

  3. 此时看到12和5都为红色,不满足红黑树的性质,此时以5节点的叔叔节点(30)为黑色,将父节点(12)变黑,将爷爷节点(19)变红,再对爷爷节点19进行右旋,如图
    红黑树平衡经典案例(图解)及插入Java实现(代码)_第4张图片

  4. 平衡了,出现了叔叔节点在左子树或右子树的情况,其实方法大致相同,可根据上述几种情况推出

直接上代码(详细注释)

package com.kangin.util;

/**
 * 1.创建Rtree,定义颜色
 * 2.创建RBNode
 * 3.辅助方法定义:parentOf(node),isRed(node),setRed(node),setBlack(node),inOrderPrint()
 * 4.左旋方法定义:leftRotate(node)
 * 5.右旋方法定义:RightRotate(node)
 * 6.公开插入接口方法定义:insert(K key, V value);
 * 7.内部插入接口方法定义:insert(RBNode node);
 * 8.修正插入导致红黑树失衡的方法定义:insertFIxUp(RBNode node);
 * 9.测试红黑树正确性
 *
 * @author khb
 * @date 2021/1/27
 */
public class MyRBtree<K extends Comparable<K>, V> {
     
    private static final boolean RED = true;
    private static final boolean BLACK = false;

    private MyRBNode root; // 根节点

    /**
     * insert的开放方法
     *
     * @param key
     * @param value
     */
    public void insert(K key, V value) {
     
        MyRBNode node = new MyRBNode();
        node.setKey(key);
        node.setValue(value);
        node.setColor(RED);
        insert(node);
    }

    /**
     * 插入节点
     *
     * @param node
     */
    private void insert(MyRBNode node) {
     
        //查找要放位置的父节点
        MyRBNode parent = null;
        MyRBNode x = this.root;
        while (x != null) {
     
            parent = x;

            int cmp = node.key.compareTo(x.key);
            //cmp > 0 在右边查找
            //cmp = 0 替换值
            //cmp < 0 在左边查找
            if (cmp > 0) {
     
                x = x.right;
            } else if (cmp == 0) {
     
                x.setValue(node.getValue());
                return;
            } else {
     
                x = x.left;
            }
        }
        node.parent = parent;
        int cmp = node.key.compareTo(parent.key);
        if (cmp > 0) {
     
            parent.right = node;
        } else {
     
            parent.left = node;
        }
        insertFIxUp(node);
    }

    /**
     * 平衡红黑树的方法
     * 1.为根节点
     * 2.相等
     * 3.父亲为黑色
     *
     * @param node
     * @return
     */
    private void insertFIxUp(MyRBNode node) {
     
        this.root.color = BLACK;

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

        //-------------------------------parent为空代表根节点,不予处理,若父节点为黑色,也不予处理----------------------------------------------------------------/
        if (parent != null && isRed(parent)) {
     
            MyRBNode uncle = null;
            if (parent == gparent.left) {
     

                //-------------------------------当叔叔节点是右子树时,即父节点处在左子树上----------------------------------------------------------------/
                uncle = gparent.right;
                //------------1.如果父亲是红,叔叔也是红   将父亲叔叔变成黑色,将爷爷变成红色,用爷爷节点进入下一个循环--------------------------------------------/
                if (uncle != null && isRed(uncle)) {
     
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gparent);
                    insertFIxUp(gparent);
                    return;
                }
                if (uncle == null || isBlack(uncle)) {
     
                    //如果叔叔为黑色或者不存在时,判断在左子树还是右子树
                    if (node == parent.left) {
     //如果是左子树,将他的父节点变黑,爷爷节点变红,以爷爷节点进行右旋
                        setBlack(parent);
                        setRed(gparent);
                        rightRotate(gparent);
                        return;
                    } else {
     
                        leftRotate(parent);
                        insertFIxUp(parent);
                        return;
                    }
                }
            } else {
     
                //-------------------------------当叔叔节点是左子树时,即父节点处在右子树上----------------------------------------------------------------/
                uncle = gparent.left;
                //如果父亲是红,叔叔也是红   将父亲叔叔变成黑色,将爷爷变成红色,用爷爷节点进入下一个循环
                if (uncle != null && isRed(uncle)) {
     
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(gparent);
                    insertFIxUp(gparent);
                    return;
                }
                if (uncle == null || isBlack(uncle)) {
     
                    if (node == parent.right) {
     //如果是右子树,将他的父节点变黑,爷爷节点变红,以爷爷节点进行左旋
                        setBlack(parent);
                        setRed(gparent);
                        leftRotate(gparent);
                        return;
                    }
                    if (node == parent.left) {
     
                        rightRotate(parent);
                        insertFIxUp(parent);
                        return;
                    }
                }
            }


        }
    }

    //获取父节点
    private MyRBNode parentOf(MyRBNode node) {
     
        if (node != null) {
     
            return node.parent;
        }
        return null;
    }

    /**
     * 判断该节点是否为红节点
     *
     * @param node
     * @return
     */
    private boolean isRed(MyRBNode node) {
     
        if (node != null) {
     
            return node.color == RED;
        }
        return false;
    }

    /**
     * 判断该节点是否为黑节点
     *
     * @param node
     * @return
     */
    private boolean isBlack(MyRBNode node) {
     
        if (node != null) {
     
            return node.color == BLACK;
        }
        return false;
    }


    /**
     * 置为红色,置为黑色
     *
     * @param node
     */
    private void setRed(MyRBNode node) {
     
        if (node != null) {
     
            node.color = RED;
        }
    }

    private void setBlack(MyRBNode node) {
     
        if (node != null) {
     
            node.color = BLACK;
        }
    }

    /**
     * 左旋 :    p                      p
     *            |                      |
     *            x                      y
     *           / \     ---->          / \
     *          lx   y                  x  ry
     *              / \                / \
     *             ly  ry             lx ly
     * 

* 1.将x的右节点指向y的左节点,将y的左节点的父节点指向x * 2. 当p不为空时,将y的父节点指向x的父节点,判断左右子树将此时y的父节点左或者右指向y, * 当p为空时,更新root节点,并将y的parent置空 * 3.将x的父节点指向y,将y的左子树指向x */ private void leftRotate(MyRBNode x) { MyRBNode y = x.right; //1.将x的右节点指向y的左节点,将y的左节点的父节点指向x x.right = y.left; if (y.left != null) { y.left.parent = x; } //2. 当p不为空时,将y的父节点指向x的父节点,判断左右子树将此时y的父节点左或者右指向y, // 当p为空时,更新root节点,并将y的parent置空 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; this.root.parent = null; } //3.将x的父节点指向y,将y的左子树指向x x.parent = y; y.left = x; } /** * 右旋方法 * 右旋示意图 * p p * | | * y x * / \ ------> / \ * x ry lx y * / \ / \ * lx rx rx ry *

* 1.将y的左子树指向x的右子树,将x的右子树的父节点指向y * 2.当p不为空时,将x的父节点指向y的父节点,此处判断x为其父节点的左右节点,再将x的父节点的左右节点指向y * 当p为空时。根节点就为x * 3.将y的父节点指向x,将x的右子树指向y */ private void rightRotate(MyRBNode y) { MyRBNode x = y.left; //1.将y的左子树指向x的右子树,将x的右子树的父节点指向y y.left = x.right; if (x.right != null) { x.right.parent = y; } //2.当p不为空时,将x的父节点指向y的父节点,此处判断x为其父节点的左右节点,再将x的父节点的左右节点指向y if (y.parent != null) { x.parent = y.parent; if (y == y.parent.left) { y.parent.left = x; } else { y.parent.right = x; } } else { // 当p为空时。根节点就为x this.root = x; this.root.parent = null; } //3.将y的父节点指向x,将x的右子树指向y y.parent = x; x.right = y; } //节点内部类 static class MyRBNode<K extends Comparable<K>, V> { private MyRBNode parent; private MyRBNode left; private MyRBNode right; private boolean color; private K key; private V value; public MyRBNode() { } public MyRBNode(MyRBNode parent, MyRBNode left, MyRBNode 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 MyRBNode getParent() { return parent; } public void setParent(MyRBNode parent) { this.parent = parent; } public MyRBNode getLeft() { return left; } public void setLeft(MyRBNode left) { this.left = left; } public MyRBNode getRight() { return right; } public void setRight(MyRBNode right) { this.right = right; } public boolean isColor() { return color; } public void setColor(boolean color) { this.color = color; } public K getKey() { return key; } public void setKey(K key) { this.key = key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } } }

你可能感兴趣的:(随记,算法学习,算法,二叉树,数据结构,java)