红黑树笔记

二叉排序树在某些情况下层级过深,所以在查找的时候效率不好。

二叉平衡树在二叉排序树的基础上进行了优化,两边的平衡因子最大不能超过1,在查找的时候效率比较好,但是在删除节点的时候就会非常糟糕,因为每次删除节点可能会伴随着整棵树的移动(如:左平衡操作,右平衡操作,平衡因子修改)。

红黑树在二叉平衡树的基础上又进行了改进,它的左右子树的层次最多不超过一倍,删除节点时只需要找到此节点的后继节点,把后继节点的值赋值到要删除的节点,然后把后继节点删除,这样就省去了许多繁琐的平衡操作。

红黑树定义:它或者是一颗空树,或者是有以下性质的一棵二叉查找树:

                      1.节点非红即黑。

                      2.根节点是黑色。

                      3.所有NULL节点称为叶子节点,且默认颜色为黑色。

                      4.所有红结点的子节点必须为黑色。

                      5.从任意一节点到其叶子节点的所有路径上都包含相同个数的黑节点(如下图,假如从根节点出发,那么到所有                               null节点的路径上都有相同数量的黑节点)。

如图:

满足以上五个条件就是红黑树。

节点插入:先按照二叉排序树的方式插入一个节点,再查找最小不平衡子树,以最小不平衡子树进行下面的操作:

1.插入的是根节点(原本树中没有节点):那么就直接将节点涂黑。

2.插入节点的父节点是黑色:如果满足以上五个条件就不需要调整。

3.插入节点的父节点是红色

    1)父节点是爷爷节点的左孩子:

         情况1:爷爷节点的另一个子节点(叔叔节点)是红色,对策: 将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把                          当前节点指向祖父节点,从新的当前节点开始算法

         情况2:叔叔节点是黑色:

                      情况2.1:当前节点是其父节点的右孩子,对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左                                            旋。

                      情况2.2:当前节点是其父节点的左孩子,对策:父节点变为黑色,祖父节点变红色,再祖父节点为支点进行右                                            旋。

  2)父节点是爷爷节点的右孩子:和上面情况一样,将左全部变成右即可

删除节点:先进行二叉排序树的删除操作,然后已替换节点作为当前节点进行后面的平衡操作

1. 当前节点是红色 :对策:直接把当前节点染成黑色,结束。

2. 当前节点x是黑色:

    1)被删除节点是父节点的左孩子

        (1)当前节点是根节点,对策:什么都不做。

        (2)当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑),对策:把父节点染成红色,兄弟节点染成黑                       色,对父节点进行左旋,重新设置x的兄弟节点。

        (3)当前节点x 的兄弟节点是黑色:

                  -- 兄弟节点的两个孩子都是黑色,对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点。

                  -- 兄弟的右孩子是黑色,左孩子是红色,对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟                         节点右旋,右旋后,重新设置x的兄弟节点。

                  -- 兄弟节点的右孩子是红色,对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右                        孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算。

  1)被删除节点是父节点的右孩子

       对策: 把上面的左设置为右

就是这么一套逻辑,根据不同的情况进行不同的处理就行了。

/**
 * 红黑树
 */
public class BlackRedTree<K, V> {
    private static final boolean RED = false;
    private static final boolean BLACK = true;
    private int modCount, size = 0;
    //根节点
    BlackRedTreeNode<K, V> rootNode;


    public V deleteNode(K key) {
        //查找到需要删除的结点
        BlackRedTreeNode<K, V> p = getEntry(key);
        if (p == null)
            return null;
        //获取需要删除结点的值
        V oldValue = p.value;
        //删除结点逻辑
        deleteEntry(p);
        return oldValue;
    }

    private void deleteEntry(BlackRedTreeNode<K, V> p) {
        modCount++;
        size--;
        //如果需要删除的结点既有左子树又有右子树
        if (p.leftNode != null && p.rightNode != null) {
            //获取到该结点的后继结点(即比该结点大一位的结点)
            BlackRedTreeNode<K, V> s = successor(p);
            //把后继的值赋值给当前结点
            p.key = s.key;
            p.value = s.value;
            //把p指向后继结点的位置
            p = s;
        }
        //如果要删除的结点只有左孩子或者只有右孩子(如果左右孩子都有,也会执行这里删除p结点)
        BlackRedTreeNode<K, V> replacement = (p.leftNode != null ? p.leftNode : p.rightNode);
        //replacement为空说明后继结点为叶子结点或者根结点(没有左子树也没有右子树)
        //replacement不为空,说明replacement有左孩子或者右孩子
        if (replacement != null) {
            //把p结点的孩子的父节点指向p的父节点
            replacement.parentNode = p.parentNode;
            //如果p的父节点为空,说明p就是根节点,那么直接把p的孩子赋值为根节点
            if (p.parentNode == null)
                rootNode = replacement;

            else if (p == p.parentNode.leftNode)
                //如果p是父节点的左孩子,那么就把p的孩子的父节点指向p的父节点的左孩子
                p.parentNode.leftNode = replacement;
            else
                //如果p是父节点的右孩子,那么就把p的孩子的父节点指向p的父节点的右孩子
                p.parentNode.rightNode = replacement;

            //断开p的所有引用,删除p结点
            p.leftNode = p.rightNode = p.parentNode = null;
            //如果被插入的结点为黑色
            if (p.color == BLACK)
                fixAfterDeletion(replacement);
        } else if (p.parentNode == null) { // 如果被删除的是根节点
            rootNode = null;
        } else { //  如果被删除的是叶子结点
            if (p.color == BLACK)
                fixAfterDeletion(p);
            if (p.parentNode != null) {
                //如果是父节点的左孩子
                if (p == p.parentNode.leftNode)
                    p.parentNode.leftNode = null;
                else if (p == p.parentNode.rightNode)
                    //如果是父节点的右孩子
                    p.parentNode.rightNode = null;
                //断开对p的引用
                p.parentNode = null;
            }
        }

    }

    /**
     * From CLR
     */
    private void fixAfterDeletion(BlackRedTreeNode<K, V> x) {
        //当x不是根结点并且x的颜色为黑色
        while (x != rootNode && x.color == BLACK) {
            //如果被删除结点是父节点的左孩子
            if (x == x.parentNode.leftNode) {
                //获取到x父节点的右孩子(x的兄弟结点)
                BlackRedTreeNode<K, V> sib = x.parentNode.rightNode;
                //如果x的兄弟结点为红色
                if (sib.color == RED) {
                    //把X的兄弟结点变为黑色
                    sib.color = BLACK;
                    //把x的父亲变为红色
                    x.parentNode.color = RED;
                    //进行一次左旋转
                    rotateLeft(x.parentNode);
                    //把x的兄弟结点重新赋值(即赋值为旋转后的x的兄弟结点)
                    sib = x.parentNode.rightNode;
                }
                //如果x的兄弟结点的左右孩子都为黑色,那就把兄弟結點设置为红色,并把x指针指向x的父亲
                //对策:将x的兄弟节点设为红色,设置x的父节点为新的x节点
                if (sib.leftNode.color == BLACK &&
                        sib.rightNode.color == BLACK) {
                    sib.color = RED;
                    x = x.parentNode;
                } else {
                    //如果x的兄弟结点的右孩子为黑色
                    //对策:将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点右旋,右旋后,重新设置x的兄弟节点。
                    if (sib.rightNode.color == BLACK) {
                        //将x兄弟节点的左孩子设为黑色
                        sib.leftNode.color = BLACK;
                        //将x兄弟节点设置红色
                        sib.color = RED;
                        //右旋后
                        rotateRight(sib);
                        //重新设置x的兄弟节点
                        sib = x.parentNode.rightNode;
                    }
                    //兄弟节点的右孩子是红色
                    //对策:把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算
                    sib.color = x.parentNode.color;
                    //把当前节点父节点染成黑色
                    x.parentNode.color = BLACK;
                    //兄弟节点右孩子染成黑色
                    sib.rightNode.color = BLACK;
                    //再以当前节点的父节点为支点进行左旋
                    rotateLeft(x.parentNode);
                    x = rootNode;
                }
            } else { // 如果被删除结点是父节点的右孩子
                //注释同上, 把上面的左设置为右即可
                BlackRedTreeNode<K, V> sib = x.parentNode.leftNode;
                if (sib.color == RED) {
                    sib.color = BLACK;
                    x.parentNode.color = RED;
                    rotateRight(x.parentNode);
                    sib = x.parentNode.leftNode;
                }

                if (sib.rightNode.color == BLACK &&
                        sib.leftNode.color == BLACK) {
                    sib.color = RED;
                    x = x.parentNode;
                } else {
                    if (sib.leftNode.color == BLACK) {
                        sib.rightNode.color = BLACK;
                        sib.color = RED;
                        rotateLeft(sib);
                        sib = x.parentNode.leftNode;
                    }
                    sib.color = x.parentNode.color;
                    x.parentNode.color = BLACK;
                    sib.leftNode.color = BLACK;
                    rotateRight(x.parentNode);
                    x = rootNode;
                }
            }
        }
        x.color = BLACK;
    }

    /**
     * 查找结点的后继结点
     *
     * @param t
     * @param <K>
     * @param <V>
     * @return
     */
    public <K, V> BlackRedTreeNode<K, V> successor(BlackRedTreeNode<K, V> t) {
        if (t == null)
            return null;
        else if (t.rightNode != null) {
            BlackRedTreeNode<K, V> p = t.rightNode;
            while (p.leftNode != null)
                p = p.leftNode;
            return p;
        } else {
            BlackRedTreeNode<K, V> p = t.parentNode;
            BlackRedTreeNode<K, V> ch = t;
            while (p != null && ch == p.rightNode) {
                ch = p;
                p = p.parentNode;
            }
            return p;
        }
    }

    /**
     * 查找到需要删除的结点
     *
     * @param key
     * @return
     */
    final BlackRedTreeNode<K, V> getEntry(Object key) {
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
        BlackRedTreeNode<K, V> p = rootNode;
        while (p != null) {
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.leftNode;
            else if (cmp > 0)
                p = p.rightNode;
            else
                return p;
        }
        return null;
    }

    /**
     * 新增结点
     *
     * @param key
     * @param value
     * @return
     */
    public V put(K key, V value) {
        BlackRedTreeNode<K, V> t = rootNode;
        if (t == null) {
            //情况1: 空树,直接添加(结点默认为黑色)
            rootNode = new BlackRedTreeNode(key, value, null);
            size = 1;
            modCount++;
        } else {
            //情况2:不是空树
            //开始寻找要插入的位置
            int cmp = 0;
            BlackRedTreeNode<K, V> parent;
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.leftNode;
                else if (cmp > 0)
                    t = t.rightNode;
                else
                    return t.setValue(value);
            } while (t != null);
            //找到要插入的位置t
            BlackRedTreeNode<K, V> newNode = new BlackRedTreeNode<>(key, value, parent);
            if (cmp < 0) {//放在parent的左孩子
                parent.leftNode = newNode;
            }
            if (cmp > 0) {//放在parent的右孩子
                parent.rightNode = newNode;
            }
            //插入数据完毕,开始调整树的平衡
            fixInsertion(newNode);
            size++;
            modCount++;
        }
        return null;
    }

    /**
     * 调整红黑以及平衡度
     *
     * @param x
     */
    public void fixInsertion(BlackRedTreeNode<K, V> x) {
        //新加的节点必须为红色
        x.color = RED;
        //如果插入结点的父节点为红色
        while (x != null && x != rootNode && x.parentNode.color == RED) {
            //如果插入结点的父节点是它爷爷结点的左孩子
            if (x.parentNode == x.parentNode.parentNode.leftNode) {
                //获取到它的叔叔结点
                BlackRedTreeNode<K, V> y = x.parentNode.parentNode.rightNode;
                //如果叔叔结点为红色
                if (y != null && y.color == RED) {
                    //把它的父亲和叔叔变黑,把它的爷爷变红
                    x.parentNode.color = BLACK;
                    y.color = BLACK;
                    y.parentNode.color = RED;
                    //把指针指向爷爷结点
                    x = x.parentNode.parentNode;
                } else {//如果叔叔结点为黑色
                    if (x == x.parentNode.rightNode) {//如果当前结点是其父节点的右孩子
                        //把指针指向它的父亲
                        x = x.parentNode;
                        //进行一次左旋转
                        rotateLeft(x);
                    }
                    //把它的父亲变黑,把它的爷爷变红
                    x.parentNode.color = BLACK;
                    x.parentNode.parentNode.color = RED;
                    //进行一次右旋转
                    rotateRight(x);
                }
            } else { //如果插入结点的父节点是它爷爷结点的右孩子-------------------------------------------------------
                //获取到爷爷结点的左孩子(左叔叔结点)
                BlackRedTreeNode<K, V> y = x.parentNode.parentNode.leftNode;
                //如果叔叔结点为红色
                if (y.color == RED) {
                    //把父亲和叔叔结点变为黑色,把爷爷结点变为红色
                    x.parentNode.color = BLACK;
                    y.color = BLACK;
                    y.parentNode.color = RED;
                    x = y.parentNode;
                } else {//如果叔叔结点为黑色
                    if (x == x.parentNode.leftNode) {//如果当前结点是其父节点的左孩子
                        //把指针指向它的父亲
                        x = x.parentNode;
                        //进行一次右旋转
                        rotateRight(x);
                    }
                    //把它的父亲变黑,把它的爷爷变红
                    x.parentNode.color = BLACK;
                    x.parentNode.parentNode.color = RED;
                    //进行一次右旋转
                    rotateLeft(x);
                }
            }
        }
        //把根结点变黑色
        rootNode.color = BLACK;
    }

    /**
     * 右旋转
     *
     * @param p
     */
    private void rotateRight(BlackRedTreeNode<K, V> p) {
        if (p != null) {
            BlackRedTreeNode<K, V> l = p.leftNode;
            p.leftNode = l.rightNode;
            if (l.rightNode != null) l.rightNode.parentNode = p;
            l.parentNode = p.parentNode;
            if (p.parentNode == null)
                rootNode = l;
            else if (p.parentNode.leftNode == p)
                p.parentNode.rightNode = l;
            else p.parentNode.leftNode = l;
            l.rightNode = p;
            p.parentNode = l;
        }
    }

    /**
     * 左旋转
     *
     * @param p
     */
    private void rotateLeft(BlackRedTreeNode<K, V> p) {
        if (p != null) {
            BlackRedTreeNode<K, V> r = p.rightNode;
            p.rightNode = r.leftNode;
            if (r.leftNode != null)
                r.leftNode.parentNode = p;
            r.parentNode = p.parentNode;
            if (p.parentNode == null)
                rootNode = r;
            else if (p.parentNode.leftNode == p)
                p.parentNode.leftNode = r;
            else
                p.parentNode.rightNode = r;
            r.leftNode = p;
            p.parentNode = r;
        }
    }

    /**
     * 节点对象
     *
     * @param <K>
     * @param <V>
     */
    public class BlackRedTreeNode<K, V> implements Map.Entry<K, V> {
        K key;
        V value;
        BlackRedTreeNode<K, V> leftNode;
        BlackRedTreeNode<K, V> rightNode;
        BlackRedTreeNode<K, V> parentNode;
        boolean color = BLACK;

        public BlackRedTreeNode(K k, V v, BlackRedTreeNode<K, V> parentNode) {
            this.key = k;
            this.value = v;
            this.parentNode = parentNode;
        }

        @Override
        public K getKey() {
            return null;
        }

        @Override
        public V getValue() {
            return null;
        }

        @Override
        public V setValue(V value) {
            return null;
        }
    }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值