zoukankan      html  css  js  c++  java
  • 红黑树简介

         红黑树是一种搜索二叉树,而且也是一种平衡搜索二叉树,它可以保证在最坏的情况下的基本的操作的时间复杂度为O(lgn)。

      红黑树具有如下的性质:

    1. 每个节点要么是红色的,要不是黑色的。
    2. 根节点是黑色的
    3. 每个叶节点(NIL)是黑色的
    4. 如果一个结点是红色的,则它的两个子结点是黑色的。
    5. 对每个节点,从该节点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

    红黑树的旋转

      注意这个旋转和AVL树的旋转不一样,红黑树的旋转只有左旋和右旋。那为什么要旋转呢,因为是在插入和删除节点后,这棵树可能就不满足红黑树的性质了,所以需要做一些重新着色和旋转的操作。

    下面看一下左旋和右旋的示意图。

    左旋可以理解为,以那个节点左旋的,这个节点就会成为自己右孩子的左孩子。

    右旋可以理解为,以那个节点右旋的,这个节点就会成为自己左孩子的右孩子。

    下面是左旋的伪代码 假设 x.right !=T.nil 且根结点的父结点为T.nil

    LEFT-ROTATE(T, x)  
     y = x.right            // x的右孩子为y
     x.right = y.left      // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子
     if y.left != T.nil          // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x
       y.left.p = x             
     y.p = x.p                   // 将 “x的父亲” 设为 “y的父亲”
    if x.p == T.nil // 如果x的父亲为null,所以x节点是根节点,这时就把y设为根节点
       T.root = y
    elseif x == x.p.left // 如果x是父节点的左孩子,就把y设置x父节点的左孩子
       x.p.left = y
    else x.p.right = y // 如果x是父节点的右孩子,就把y设置x父节点的右孩子
    y.left = x // 把x设置为y的左孩子,
    x.p = y             // 把y设为x的父亲

    右旋的伪代码

     RIGHT-ROTATE(T, y)  
     x = y.left            // y的左孩子为x
     y.left = x.right      // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为x的右孩子
     if x.right != T.nil          // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y
       x.right.p = y             
     x.p = y.p                   // 将 “y的父亲” 设为 “x的父亲”
     if y.p == T.nil             // 如果y的父亲为null,所以y节点是根节点,这时就把x设为根节点
       T.root = x
     elseif y == y.p.left        // 如果y是父节点的左孩子,就把x设置y父节点的左孩子
       y.p.left = x
     else y.p.right = x          // 如果y是父节点的右孩子,就把x设置y父节点的右孩子
     x.left = y                  // 把y设置为x的右孩子,
     y.p = x             // 把x设为y的父亲 

    红黑树的插入

    红黑树的插入前面的逻辑和普通的二叉搜索树的插入逻辑差不多。这里不再多说,主要差别在于插入后可能会破坏红黑树的性质。我们主要来看看修复的伪代码。

    先把要插入节点设为红色,为什么设为红色而不是黑色呢,这个可以先看一下红黑树的性质,如果把插入节点设置黑色,那么性质1234都不违背,但是性质5肯定违背。如果设置为红色呢,则可能违背2和4,注意是可能。也有可能插入的红色节点后,依然是一个红黑树。

    1 如果插入的节点是根节点。则把节点颜色从红色变为黑色即可。

    2 如果插入的节点不是根节点,但是待插入位置的父节点是黑色。插入红色节点不会违背任何红黑树性质。

    3 如果不是上面两种情况,则情况要复杂了。分为三种情况处理。下面先看一下伪代码。

    RB-INSERT-FIXUP(T, z)
    while z.p.color == RED                                      // 若“当前节点(z)的父节点是红色”,则进行以下处理。
        if z.p == z.p.p.left                                    // 若“z的父节点”是“z的祖父节点的左孩子”,则进行以下处理。
            y = z.p.p.right                                     // 将y设置为“z的叔叔节点(z的祖父节点的右孩子)”
            if y.color == RED                                   // Case 1条件:叔叔是红色
                z.p.color = BLACK                   ▹ Case 1   //  (01) 将“父节点”设为黑色。
                y.color = BLACK                     ▹ Case 1   //  (02) 将“叔叔节点”设为黑色。
                z.p.p.color = RED                   ▹ Case 1   //  (03) 将“祖父节点”设为“红色”。
                z = z.p.p                           ▹ Case 1   //  (04) 将“祖父节点”设为“当前节点”(红色节点)
               else if z == z.p.right                          // Case 2条件:叔叔是黑色,且当前节点是右孩子
                        z = z.p                     ▹ Case 2   //  (01) 将“父节点”作为“新的当前节点”。
                        LEFT-ROTATE(T, z)           ▹ Case 2   //  (02) 以“新的当前节点”为支点进行左旋。
                    z.p.color = BLACK               ▹ Case 3   // Case 3条件:叔叔是黑色,且当前节点是左孩子。(01) 将“父节点”设为“黑色”。
                    z.p.p.color = RED               ▹ Case 3   //  (02) 将“祖父节点”设为“红色”。
                    RIGHT-ROTATE(T, z.p.p)          ▹ Case 3   //  (03) 以“祖父节点”为支点进行右旋。
        else (same as then clause with "right" and "left" exchanged)      // 若“z的父节点”是“z的祖父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。
    T.root.color = BLACK
    RB-INSERT-FIXUP(T, z)
    while z.p.color == RED                                                  // 若“当前节点(z)的父节点是红色”,则进行以下处理。
         if z.p = z.p.p.right                                          // 若“z的父节点”是“z的祖父节点的右孩子”,则进行以下处理。
             y = z.p.p.left                                         // 将y设置为“z的叔叔节点(z的祖父节点的左孩子)”
             if y.color == RED                                         // Case 1条件:叔叔是红色
                 z.p.color = BLACK                ▹ Case 1   //  (01) 将“父节点”设为黑色。
                 y.color = BLACK                  ▹ Case 1   //  (02) 将“叔叔节点”设为黑色。
                 z.p.p.color = RED                ▹ Case 1   //  (03) 将“祖父节点”设为“红色”。
                 z = z.p.p                        ▹ Case 1   //  (04) 将“祖父节点”设为“当前节点”(红色节点)
             else if z == z.p.left                                // Case 2条件:叔叔是黑色,且当前节点是左孩子
                      z = z.p                     ▹ Case 2   //  (01) 将“父节点”作为“新的当前节点”。
                      RIGHT-ROTATE(T, z)          ▹ Case 2   // (02) 以“新的当前节点”为支点进行右旋。
                 z.p.color = BLACK                ▹ Case 3   // Case 3条件:叔叔是黑色,且当前节点是右孩子。(01) 将“父节点”设为“黑色”。
                 z.p.p.color = RED                ▹ Case 3   //  (02) 将“祖父节点”设为“红色”。
                 LEFT-ROTATE(T, z.p.p)            ▹ Case 3   //  (03) 以“祖父节点”为支点进行左旋。
         else (same as then clause with "right" and "left" exchanged)      // 若“z的父节点”是“z的祖父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。
    T.root.color = BLACK

     1 当父节点是祖父节点的左孩子时

      条件 处理策略
    case1 父节点是红色,叔叔节点红色

    1 将父节点设置黑色

    2 将叔叔节点设置黑色

    3 将祖父节点设置红色

    4将祖父节点设为当前节点继续

    case2 父节点是红色,叔叔是黑色,且当前节点是右孩子

    1 将“父节点”作为“新的当前节点”。

    2 以“新的当前节点”为支点进行左旋

    case3 父节点是红色,叔叔是黑色,且当前节点是左孩子

    1 以“新的当前节点”将“父节点”设为“黑色”

    2 将“祖父节点”设为“红色”

    3 以“祖父节点”为支点进行右旋

     2 当父节点是祖父节点的右孩子时

      条件
    处理策略
    case1 父节点是红色,叔叔节点红色

    1 将父节点设置黑色

    2 将叔叔节点设置黑色

    3 将祖父节点设置红色

    4将祖父节点设为当前节点继续

    case2 父节点是红色,叔叔是黑色,且当前节点是右孩子

    1 将“父节点”作为“新的当前节点”。

    2 以“新的当前节点”为支点进行右旋

    case3 父节点是红色,叔叔是黑色,且当前节点是左孩子

    1 以“新的当前节点”将“父节点”设为“黑色”

    2 将“祖父节点”设为“红色”

    3 以“祖父节点”为支点进行左旋

    这就是所有的关于红黑树的应对情况。其实不用太紧张,这就像是公式一样,遇到什么情况,做什么操作是一定的。所以不太明白的话,就动手做几遍。就会加深理解了,

    红黑树的删除

     删除操作相对于插入要更复杂一些。想像一下,在二叉树删除时,我们分为三种情况,删除节点只有左孩子,删除节点只有右孩子,删除节点有两个孩子的情况,其中删除节点有两个孩子节点的时候,需要找出

    删除节点的后继节点,这时后继节点肯定只有右孩子,把后继节点移到待删除的节点,其实就相当于删除了后继节点。然后删除又转化为只有右孩子的情况。

    而红黑树的删除只是在这个情况上,多了一个颜色属性,还有删除可能违背红黑树的性质,那什么情况下删除节点会违背红黑树的性质呢。当删除的节点颜色是黑色的时候,会违背红黑树的性质。

    为什么删除红色节点不会违背呢,

    那就看看红黑树的性质,看看会不会违背吧,性质1肯定不会,因为删除前是一个红黑树,删除节点肯定不会破坏这个性质,因为删除的节点的红色的,所以删除的节点不是根节点,根节点依然是黑色的。性质3也肯定不会违背了。

    性质4,也不会,因为删除的红色节点,所以删除节点的子节点和父节点都是黑色的。性质5也不会,因为删除的是红色节点。不会影响每条路径上黑色节点的数目。 

    那么删除节点为黑色有分为那些情况呢,

    1 当删除的节点为根节点时,如果是它的红色孩子节点移到根节点,这时只要把节点置为黑色即可,

    2 当删除节点不是根节点。那么如果它的孩子节点是红色的,这时也是只要把节点置为黑色即可。

    如果不是以上两种情况,就有点复杂了,先看下面的伪代码。

    RB-DELETE-FIXUP(T, x)
    while x ≠ T.root and x.color == BLACK  
          if x == x.p.left                                   // 如果x是它父亲的左孩子
               w = x.p.right                                               // 若 “x”是“它父节点的左孩子”,则设置 “w”为“x的兄弟”(即w为x父节点的右孩子)                                          
               if w.color == RED                                           // Case 1: x是“黑+黑”节点,x的兄弟是红色。(此时x的父节点和x的叔叔节点的子节点都是黑节点)。
                   w.color = BLACK                         ▹  Case 1   //   (01) 将x的兄弟节点设为“黑色”。
                   x.p.color = RED                         ▹  Case 1   //   (02) 将x的父节点设为“红色”。
                   LEFT-ROTATE(T, x.p)                     ▹  Case 1   //   (03) 对x的父节点进行左旋。
                   w = x.p.right                           ▹  Case 1   //   (04) 左旋后,重新设置x的兄弟节点。
               if w.left.color == BLACK and w.right.color == BLACK         // Case 2: x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。
                   w.color = RED                           ▹  Case 2   //   (01) 将x的兄弟节点设为“红色”。
                   x =  x.p                                ▹  Case 2   //   (02) 设置“x的父节点”为“新的x节点”。
                  else if w.right.color == BLACK                          // Case 3: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。
                           w.left.color = BLACK            ▹  Case 3   //   (01) 将x兄弟节点的左孩子设为“黑色”。
                           w.color = RED                   ▹  Case 3   //   (02) 将x兄弟节点设为“红色”。
                           RIGHT-ROTATE(T, w)              ▹  Case 3   //   (03) 对x的兄弟节点进行右旋。
                           w = x.p.right                   ▹  Case 3   //   (04) 右旋后,重新设置x的兄弟节点。
                        w.color = x.p.color                    ▹  Case 4   // Case 4: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的。(01) 将x父节点颜色 赋值给 x的兄弟节点。
                        x.p.color = BLACK                      ▹  Case 4   //   (02) 将x父节点设为“黑色”。
                        w.right.color = BLACK                  ▹  Case 4   //   (03) 将x兄弟节点的右子节设为“黑色”。
                        LEFT-ROTATE(T, x.p)                    ▹  Case 4   //   (04) 对x的父节点进行左旋。
                        x = T.root                             ▹  Case 4   //   (05) 设置“x”为“根节点”。
           else (same as then clause with "right" and "left" exchanged)        // 若 “x”是“它父节点的右孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。
    x.color = BLACK
    RB-DELETE-FIXUP(T, x)
    while x ≠ T.root and x.color == BLACK  
          if x == x.p.right                                   // 如果x是它父亲的右孩子
               w = x.p.left                                               // 若 “x”是“它父节点的右孩子”,则设置 “w”为“x的兄弟”(即w为x父节点的左孩子)                                          
               if w.color == RED                                           // Case 1: x是“黑+黑”节点,x的兄弟是红色。(此时x的父节点和x的叔叔节点的子节点都是黑节点)。
                   w.color = BLACK                         ▹  Case 1   //   (01) 将x的兄弟节点设为“黑色”。
                   x.p.color = RED                         ▹  Case 1   //   (02) 将x的父节点设为“红色”。
                   RIGHT-ROTATE(T, x.p)                     ▹  Case 1   //   (03) 对x的父节点进行右旋。
                   w = x.p.left                           ▹  Case 1   //   (04) 右旋后,重新设置x的兄弟节点。
               if w.left.color == BLACK and w.right.color == BLACK         // Case 2: x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。
                   w.color = RED                           ▹  Case 2   //   (01) 将x的兄弟节点设为“红色”。
                   x =  x.p                                ▹  Case 2   //   (02) 设置“x的父节点”为“新的x节点”。
                  else if w.left.color == BLACK                          // Case 3: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色,左孩子是黑色的。
                           w.right.color = BLACK            ▹  Case 3   //   (01) 将x兄弟节点的右孩子设为“黑色”。
                           w.color = RED                   ▹  Case 3   //   (02) 将x兄弟节点设为“红色”。
                           LEFT-ROTATE(T, w)              ▹  Case 3   //   (03) 对x的兄弟节点进行左旋。
                           w = x.p.left                   ▹  Case 3   //   (04) 左旋后,重新设置x的兄弟节点。
                        w.color = x.p.color                    ▹  Case 4   // Case 4: x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色的。(01) 将x父节点颜色 赋值给 x的兄弟节点。
                        x.p.color = BLACK                      ▹  Case 4   //   (02) 将x父节点设为“黑色”。
                        w.left.color = BLACK                  ▹  Case 4   //   (03) 将x兄弟节点的左子节设为“黑色”。
                        RIGHT-ROTATE(T, x.p)                    ▹  Case 4   //   (04) 对x的父节点进行右旋。
                        x = T.root                             ▹  Case 4   //   (05) 设置“x”为“根节点”。
           else (same as then clause with "right" and "left" exchanged)        // 若 “x”是“它父节点的左孩子”,将上面的操作中“right”和“left”交换位置,然后依次执行。
    x.color = BLACK

     前提条件 x节点不是根节点,并且x的颜色是红色的。并且x是父节点的左孩子

      条件
    处理策略
    case1 兄弟节点是红色的

    1 将x的兄弟节点设为黑色。

    2 将x的父节点设为红色。

    3 对x的父节点进行左旋。

    4 左旋后,重新设置x的兄弟节点。

    case2 x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色

    1 将x的兄弟节点设为红色。

    2 设置“x的父节点”为“新的x节点”。

    case3 x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。

    1 将x兄弟节点的左孩子设为“黑色”。

    2 将x兄弟节点设为红色。

    3 对x的兄弟节点进行右旋。

    4 右旋后,重新设置x的兄弟节点。

    case4 x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。

    1 将x父节点颜色 赋值给 x的兄弟节点。

    2 将x父节点设为“黑色”。

    3 将x兄弟节点的右子节设为“黑色”。

    4 对x的父节点进行左旋。

    5 设置“x”为“根节点”。

     前提条件 x节点不是根节点,并且x的颜色是红色的。并且x是父节点的右孩子

      条件
    处理策略
    case1 兄弟节点是红色的

    1 将x的兄弟节点设为黑色。

    2 将x的父节点设为红色。

    3 对x的父节点进行右旋。

    4 右旋后,重新设置x的兄弟节点。

    case2 x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色

    1 将x的兄弟节点设为红色。

    2 设置“x的父节点”为“新的x节点”。

    case3 x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。

    1 将x兄弟节点的右孩子设为“黑色”。

    2 将x兄弟节点设为红色。

    3 对x的兄弟节点进行左旋。

    4 左旋后,重新设置x的兄弟节点。

    case4 x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。

    1 将x父节点颜色 赋值给 x的兄弟节点。

    2 将x父节点设为“黑色”。

    3 将x兄弟节点的左子节设为“黑色”。

    4 对x的父节点进行右旋。

    5 设置“x”为“根节点”。

    JAVA版本实现

    package cn.damai.maitix.tic.devut.service;
    
    /**
     * Created by dupang on 2017/11/12.
     */
    public class RBTreeNode<T> {
    
    
        /**
         * 节点的左孩子
         */
        private RBTreeNode<T> left;
        /**
         * 节点的右孩子
         */
        private RBTreeNode<T> right;
        /**
         * 节点的父亲
         */
        private RBTreeNode<T> parent;
        /**
         * 节点的颜色
         */
        private Boolean color ;
        /**
         * 节点的值
         */
        private T value;
    
        public RBTreeNode() {
    
        }
    
        public RBTreeNode(RBTreeNode<T> left, RBTreeNode<T> right, RBTreeNode<T> parent, T value, Boolean color) {
            this.left = left;
            this.right = right;
            this.parent = parent;
            this.value = value;
            this.color = color;
        }
    
        public RBTreeNode<T> getLeft() {
            return left;
        }
    
        public void setLeft(RBTreeNode<T> left) {
            this.left = left;
        }
    
        public RBTreeNode<T> getRight() {
            return right;
        }
    
        public void setRight(RBTreeNode<T> right) {
            this.right = right;
        }
    
        public RBTreeNode<T> getParent() {
            return parent;
        }
    
        public void setParent(RBTreeNode<T> parent) {
            this.parent = parent;
        }
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    
        public Boolean getColor() {
            return color;
        }
    
        public void setColor(Boolean color) {
            this.color = color;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) { return true; }
            if (o == null || getClass() != o.getClass()) { return false; }
    
            RBTreeNode<?> that = (RBTreeNode<?>)o;
    
            if (left != null ? !left.equals(that.left) : that.left != null) { return false; }
            if (right != null ? !right.equals(that.right) : that.right != null) { return false; }
            if (parent != null ? !parent.equals(that.parent) : that.parent != null) { return false; }
            if (color != null ? !color.equals(that.color) : that.color != null) { return false; }
            return value != null ? value.equals(that.value) : that.value == null;
        }
    
        @Override
        public int hashCode() {
            int result = left != null ? left.hashCode() : 0;
            result = 31 * result + (right != null ? right.hashCode() : 0);
            result = 31 * result + (parent != null ? parent.hashCode() : 0);
            result = 31 * result + (color != null ? color.hashCode() : 0);
            result = 31 * result + (value != null ? value.hashCode() : 0);
            return result;
        }
    }

    测试类

    package cn.damai.maitix.tic.devut.service;
    
    /**
     * Created by dupang on 2017/11/12.
     */
    public class RBTreeTest {
        private static final Boolean RED = true;
        private static final Boolean BLACK = false;
        private static final RBTreeNode<Integer> NULL_NODE = new RBTreeNode<>(null, null, null, null, BLACK);
    
        public static void main(String[] args) {
            int[] values = new int[] {18, 5, 3, 9, 2, 13, 32};
            //初始化一个表示树的根节点
            RBTreeNode<Integer> root = new RBTreeNode<>(NULL_NODE, NULL_NODE, NULL_NODE, null, BLACK);
    
            //遍历插入
            for (Integer value : values) {
                //初始化要插入的节点
                RBTreeNode<Integer> treeNode = new RBTreeNode<>(NULL_NODE, NULL_NODE, NULL_NODE, value, RED);
                RBTreeNode<Integer> treeNode2 = new RBTreeNode<>(NULL_NODE, NULL_NODE, NULL_NODE, value, RED);
    
                System.out.println(treeNode == treeNode2);
                rbInsert(root, treeNode);
            }
    
            //中序遍历
            inOrderTreeWalk(root);
            System.out.println();
            //先序遍历
            preOrderTreeWalk(root);
            System.out.println();
            //后序遍历
            postOrderTreeWalk(root);
            System.out.println();
    
            RBTreeNode<Integer> treeNode = treeSearch(root,3);
            //求最大值
            treeMaximum(root);
            //求最小值
            treeMinimum(root);
            //后继
            treeSuccessor(root);
            //前驱
            treePredecessor(root);
        }
    
        /**
         * 红黑树的插入操作
         *
         * @param root 二叉树的根
         * @param z    要插入的节点
         */
        public static void rbInsert(RBTreeNode<Integer> root, RBTreeNode<Integer> z) {
            //声明一个y,用于记录while循环中循环节点的位置,也就是插入节点要插入的位置
            RBTreeNode<Integer> y = NULL_NODE;
            //把树的根节点赋值给x,先从根节点开始寻找插入的点。
            RBTreeNode<Integer> x = root;
            while (!NULL_NODE.equals(x)) {
                y = x;
                //如果要插入的值比根节点的值大,
                if (z.getValue() > x.getValue()) {
                    x = x.getRight();
                } else {
                    x = x.getLeft();
                }
            }
            z.setParent(y);            // y是最后找到的插入的位置,就把z的父亲设为y。
            //如果y是null,说明是个空树
            if (y.equals(NULL_NODE)) {
                z.setParent(null);
                z.setLeft(null);
                z.setRight(null);
                z.setValue(z.getValue());
                z.setColor(BLACK);
            } else if (z.getValue() < y.getValue()) {    //如果插入的节点比y小,就把y的左孩子设为z
                y.setLeft(z);
            } else {                                 //如果插入的节点比y大,就把y的右孩子设为z
                y.setRight(z);
            }
            z.setColor(RED);                        //把插入的节点颜色设为红色
            rbInsertFixup(root, z);                  //因为插入节点后可能破坏红黑树的性质,所以调用这个方法来修正使基再次成为红黑树
        }
    
        /**
         * 插入后修正红黑树
         *
         * @param root 根节点
         * @param z    当前节点
         */
        public static void rbInsertFixup(RBTreeNode<Integer> root, RBTreeNode<Integer> z) {
            while (z.getParent().getColor() == RED) {
                if (z.getParent() == z.getParent().getParent().getLeft()) {  //如果插入节点的父节点是祖父节点的左孩子
                    RBTreeNode<Integer> y = z.getParent().getParent().getRight(); //y是z的叔叔节点
                    if (y.getColor() == RED) {                              //case 1 如果叔叔节点的颜色是红色的
                        z.getParent().setColor(BLACK);                      //case 1 把z的父节点设为黑色
                        y.setColor(BLACK);                                  //case 1 把叔叔节点设为黑色
                        z.getParent().getParent().setColor(RED);            //case 1 把祖父节点设为红色
                        z = z.getParent().getParent();                      //case 1 把祖父节点设置为当前节点继续操作
                    } else {
                        if (z == z.getParent().getRight()) {                //case 2 如果z是之父节点的右孩子
                            z = z.getParent();                              //case 2 把父节点设为当前节点继续操作
                            leftRotate(root, z);                            //case 2 把父节点设为当前节点进行左旋
                        }
                        z.getParent().setColor(BLACK);                      //case 3 设置父节点的颜色为黑色
                        z.getParent().getParent().setColor(RED);            //case 3 设置祖父节点的颜色为红色
                        rightRotate(root, z.getParent().getParent());       //case 3 把祖父节点设为当前节点进行右旋
                    }
    
                } else {                                                 //如果插入节点的父节点是祖父节点的右孩子
                    RBTreeNode<Integer> y = z.getParent().getParent().getLeft(); //y是z的叔叔节点
                    if (y.getColor() == RED) {                              //case 1 如果叔叔节点的颜色是红色的
                        z.getParent().setColor(BLACK);                      //case 1 把z的父节点设为黑色
                        y.setColor(BLACK);                                  //case 1 把叔叔节点设为黑色
                        z.getParent().getParent().setColor(RED);            //case 1 把祖父节点设为红色
                        z = z.getParent().getParent();                      //case 1 把祖父节点设置为当前节点继续操作
                    } else {
                        if (z == z.getParent().getLeft()) {                 //case 2 如果z是之父节点的左孩子
                            z = z.getParent();                              //case 2 把父节点设为当前节点继续操作
                            rightRotate(root, z);                           //case 2 把父节点设为当前节点进行右旋
                        }
                        z.getParent().setColor(BLACK);                      //case 3 设置父节点的颜色为黑色
                        z.getParent().getParent().setColor(RED);            //case 3 设置祖父节点的颜色为红色
                        leftRotate(root, z.getParent().getParent());        //case 3 把祖父节点设为当前节点进行左旋
                    }
                }
            }
        }
    
        /**
         * 左旋
         *
         * @param root 根节点
         * @param x    旋转的节点
         */
        public static void leftRotate(RBTreeNode<Integer> root, RBTreeNode<Integer> x) {
            RBTreeNode<Integer> y = x.getRight();       //假设x的右孩子是y
            x.setRight(y.getLeft());                    // y的左孩子变为x的右孩子
            if (!NULL_NODE.equals(y.getLeft())) {        // 如果y的左孩子不为null,就设置它的父节点为x
                y.getLeft().setParent(x);
            }
            y.setParent(x.getParent());                 //把y的父节点换为x的父节点
    
            if (NULL_NODE.equals(x.getParent())) {       //如果x的父节点为null,说明x是根节点,就把y设置为根节点。
                y.setParent(root.getParent());
                y.setRight(root.getRight());
                y.setLeft(root.getLeft());
                y.setColor(root.getColor());
                y.setValue(root.getValue());
            } else if (x == x.getParent().getLeft()) {  //如果x是其父节点的左孩子,就设置x的父节点的左孩子为y
                x.getParent().setLeft(y);
            } else {
                x.getParent().setRight(y);              //如果x是其父节点的右孩子,就设置x的父节点的右孩子为y
            }
            y.setLeft(x);                               //设置y的左孩子为x
            x.setParent(y);                             //设置x的父亲为y
        }
    
        /**
         * 右旋
         *
         * @param root 根节点
         * @param y    旋转的节点
         */
        public static void rightRotate(RBTreeNode<Integer> root, RBTreeNode<Integer> y) {
            RBTreeNode<Integer> x = y.getLeft();        //假设y的左孩子是x
            y.setLeft(x.getRight());                    // y的左孩子变为x的右孩子
            if (!NULL_NODE.equals(x.getRight())) {                 // 如果y的左孩子不为null,就设置它的父节点为x
                x.getRight().setParent(y);
            }
            x.setParent(y.getParent());                 //把y的父节点换为x的父节点
            if (x.getRight().equals(y.getParent())) {                //如果y的父节点为null,说明y是根节点,就把x设置为根节点。
                x.setParent(root.getParent());
                x.setRight(root.getRight());
                x.setLeft(root.getLeft());
                x.setColor(root.getColor());
                x.setValue(root.getValue());
            } else if (y == y.getParent().getLeft()) {  //如果y是其父节点的左孩子,就设置y的父节点的左孩子为x
                y.getParent().setLeft(x);
            } else {
                y.getParent().setRight(x);              //如果y是其父节点的右孩子,就设置y的父节点的右孩子为x
            }
            x.setRight(y);                              //设置x的左孩子为y
            y.setParent(x);                             //设置y的父亲为x
        }
    
        /**
         * 中序遍历
         *
         * @param treeNode 根节点
         */
        public static void inOrderTreeWalk(RBTreeNode<Integer> treeNode) {
            if (!NULL_NODE.equals(treeNode)) {
                inOrderTreeWalk(treeNode.getLeft());
                System.out.print(treeNode.getValue() + " ");
                inOrderTreeWalk(treeNode.getRight());
            }
        }
    
        /**
         * 前序遍历
         *
         * @param treeNode 根节点
         */
        public static void preOrderTreeWalk(RBTreeNode<Integer> treeNode) {
            if (treeNode != null && treeNode.getValue() != null) {
                System.out.print(treeNode.getValue() + " ");
                inOrderTreeWalk(treeNode.getLeft());
                inOrderTreeWalk(treeNode.getRight());
            }
        }
    
        /**
         * 后序遍历
         *
         * @param treeNode 根节点
         */
        public static void postOrderTreeWalk(RBTreeNode<Integer> treeNode) {
            if (!NULL_NODE.equals(treeNode)) {
                inOrderTreeWalk(treeNode.getLeft());
                inOrderTreeWalk(treeNode.getRight());
                System.out.print(treeNode.getValue() + " ");
            }
        }
    
        /**
         * 在树中查询指定值的节点
         *
         * @param treeNode 树的节点
         * @param k        要查询的值
         * @return 如果查询到节点的值等于要查询的值,就返回这个节点,否则返回null
         */
        public static RBTreeNode<Integer> treeSearch(RBTreeNode<Integer> treeNode, Integer k) {
            if (NULL_NODE.equals(treeNode) || treeNode.getValue() == k) {
                return treeNode;
            }
            if (k > treeNode.getValue()) {
                return treeSearch(treeNode.getRight(), k);
            } else {
                return treeSearch(treeNode.getLeft(), k);
            }
        }
    
        /**
         * 在树中找到最大值
         *
         * @param treeNode 树的节点
         * @return 如果查询到节点的值等于要查询的值,就返回这个节点,否则返回null
         */
        public static RBTreeNode<Integer> treeMaximum(RBTreeNode<Integer> treeNode) {
            while (!NULL_NODE.equals(treeNode.getRight())) {
                treeNode = treeNode.getRight();
            }
            return treeNode;
        }
    
        /**
         * 在树中找到最小值
         *
         * @param treeNode 树的节点
         * @return 如果查询到节点的值等于要查询的值,就返回这个节点,否则返回null
         */
        public static RBTreeNode<Integer> treeMinimum(RBTreeNode<Integer> treeNode) {
            while (!NULL_NODE.equals(treeNode.getLeft())) {
                treeNode = treeNode.getLeft();
            }
            return treeNode;
        }
    
        /**
         * 找一个节点的后继
         *
         * @return 一个节点的后继
         */
        public static RBTreeNode<Integer> treeSuccessor(RBTreeNode<Integer> treeNode) {
            if (!NULL_NODE.equals(treeNode.getRight())) {
                return treeMinimum(treeNode.getRight());
            }
            RBTreeNode<Integer> y = treeNode.getParent();
            while (y != null && treeNode == y.getRight()) {
                treeNode = y;
                y = y.getParent();
            }
            return y;
        }
    
        /**
         * 找一个节点的前驱
         *
         * @return 一个节点的前驱
         */
        public static RBTreeNode<Integer> treePredecessor(RBTreeNode<Integer> treeNode) {
            if (!NULL_NODE.equals(treeNode.getLeft())) {
                return treeMaximum(treeNode.getLeft());
            }
            RBTreeNode<Integer> y = treeNode.getParent();
            while (y != null && treeNode == y.getLeft()) {
                treeNode = y;
                y = y.getParent();
            }
            return y;
        }
    
        public static void transplant(RBTreeNode<Integer> root, RBTreeNode<Integer> target, RBTreeNode<Integer> source) {
            if (target.getParent() == null) {
                root.setValue(source.getValue());
                root.setLeft(source.getLeft());
                root.setRight(source.getRight());
                root.setParent(source.getParent());
                root.setColor(BLACK);
            } else if (target == target.getParent().getLeft()) {
                target.getParent().setLeft(source);
            } else {
                target.getParent().setRight(source);
            }
            source.setParent(target.getParent());
        }
    
        /**
         * 从一棵树中删除一个节点
         *
         * @param root 根节点
         * @param z    要删除的节点
         */
        public static void treeDelete(RBTreeNode<Integer> root, RBTreeNode<Integer> z) {
            RBTreeNode<Integer> y = z;                                  // 记录要删除的节点
            Boolean yOriginalColor = y.getColor();                      // 记录删除节点的原来的颜色
            RBTreeNode<Integer> x;                                      // x是y的右孩子
            if (NULL_NODE.equals(z.getLeft())) {                        // 如果z的左孩子为空
                x = z.getRight();                                       // 就把z的右孩子直接替换z
                transplant(root, z, z.getRight());
            } else if (NULL_NODE.equals(z.getRight())) {                // 如果z的右孩子为空
                x = z.getLeft();                                        // 就把z的左孩子直接替换z
                transplant(root, z, z.getLeft());
            } else {
                y = treeMinimum(z.getRight());                          // 如果z的两个孩子都不为空 就找到z的后继t
                yOriginalColor = y.getColor();                          // 重新记录z的后继y的颜色
                x = y.getRight();                                       // 重新记录y的右孩子
                if (y.getParent() == z) {                               // 如果后继y是z的孩子,就把y的右孩子的父亲设为y,
                    x.setParent(y);                                     // 其实这一步感觉没必要。x本来是y的右孩子,肯定x的父亲是y
                } else {                                                //
                    transplant(root, y, y.getRight());                  // 先用y的右孩子替换y
                    y.setRight(z.getRight());                           // 把y的右孩子设为z的右孩子
                    y.getRight().setParent(y);                          // 把y新的右孩子的父亲设为y
                }
                transplant(root, z, y);                                 // 用y替换z
                y.setLeft(z.getLeft());                                 // 把y的左孩子设为z的左孩子
                y.getLeft().setParent(y);                               // 把y新的左孩子的父节点设为y
                y.setColor(z.getColor());                               // 把y的颜色设置为z的颜色
            }
            if (yOriginalColor == BLACK) {                               // 如果要删除的节点的颜色是黑色的,就破坏了红黑树的
                rbDeleteFixup(root, x);                                  // 性质,需要进行修正
            }
        }
    
        /**
         * 红黑树删除后的修正
         *
         * @param root 根节点
         * @param x    删除节点的右孩子
         */
        public static void rbDeleteFixup(RBTreeNode<Integer> root, RBTreeNode<Integer> x) {
            while (x != root && x.getColor() == BLACK) {
                if (x == x.getParent().getLeft()) {                         // 如果x是其父节点的左孩子
                    RBTreeNode<Integer> w = x.getParent().getRight();       // w是x的兄弟节点
                    if (w.getColor() == RED) {                              // case1 如果x的兄弟节点是红色的
                        w.setColor(BLACK);                                  // case1 设兄弟节点w的颜色为黑色
                        x.getParent().setColor(RED);                        // case1 设x父节点的颜色为红色
                        leftRotate(root, x.getParent());                    // case1 以x的父节点左旋
                        w = x.getParent().getRight();                       // case1 重新设置x的兄弟节点w
                    }
                    if (w.getLeft().getColor() == BLACK && w.getRight().getColor() == BLACK) { // case2 如果兄弟节点w的左右孩子颜色都是黑色
                        w.setColor(RED);                                    // case2 设兄弟节点w的颜色为红色
                        x = x.getParent();                                  // case2 设x的父节点为当前节点
                    } else {
                        if (w.getRight().getColor() == BLACK) {             // case3 如果兄弟节点w的右孩子是黑色
                            w.getLeft().setColor(BLACK);                    // case3 设兄弟节点w的左孩子是黑色
                            w.setColor(RED);                                // case3 设兄弟节点w的颜色是红色
                            rightRotate(root, w);                           // case3 右旋兄弟节点w
                            w = x.getParent().getRight();                   // case3 重新设置x的兄弟节点w
                        }
                        w.setColor(x.getParent().getColor());               // case4 设兄弟节点w的颜色是x的父节点的颜色
                        x.getParent().setColor(BLACK);                      // case4 设x的父节点的颜色为黑色
                        w.getRight().setColor(BLACK);                       // case4 设兄弟节点w的右孩子颜色是黑色
                        leftRotate(root, x.getParent());                    // case4 左旋x的父节点
                        x = root;
                    }
    
                    // 设x为根节点
                } else {                                                    // 如果x是其父节点的右孩子
                    RBTreeNode<Integer> w = x.getParent().getLeft();        // w是x的兄弟节点
                    if (w.getColor() == RED) {                              // case1 如果x的兄弟节点是红色的
                        w.setColor(BLACK);                                  // case1 设兄弟节点w的颜色为黑色
                        x.getParent().setColor(RED);                        // case1 设x父节点的颜色为红色
                        rightRotate(root, x.getParent());                   // case1 以x的父节点右旋
                        w = x.getParent().getLeft();                        // case1 重新设置x的兄弟节点w
                    }
                    if (w.getLeft().getColor() == BLACK && w.getRight().getColor() == BLACK) { // case2 如果兄弟节点w的左右孩子颜色都是黑色
                        w.setColor(RED);                                    // case2 设兄弟节点w的颜色为红色
                        x = x.getParent();                                  // case2 设x的父节点为当前节点
                    } else {
                        if (w.getLeft().getColor() == BLACK) {              // case3 如果兄弟节点w的左孩子是黑色
                            w.getRight().setColor(BLACK);                   // case3 设兄弟节点w的右孩子是黑色
                            w.setColor(RED);                                // case3 设兄弟节点w的颜色是红色
                            leftRotate(root, w);                            // case3 左旋兄弟节点w
                            w = x.getParent().getLeft();                    // case3 重新设置x的兄弟节点w
                        }
                        w.setColor(x.getParent().getColor());               // case4 设兄弟节点w的颜色是x的父节点的颜色
                        x.getParent().setColor(BLACK);                      // case4 设x的父节点的颜色为黑色
                        w.getLeft().setColor(BLACK);                        // case4 设兄弟节点w的左孩子颜色是黑色
                        rightRotate(root, x.getParent());                   // case4 右旋x的父节点
                        x = root;                                           // 设x为根节点
                    }
                }
                x.setColor(BLACK);
            }
        }
    }
  • 相关阅读:
    Mac下终端常用命令
    mac上完整卸载删除.简单粗暴无脑:androidstudio删除方案
    Mac版 Intellij IDEA 激活
    解决Pods Unable to find a specification for `xxxxx`问题
    java并发编程(十五)内存可见两种方式 加锁和volatile
    java并发编程(五)正确使用volatile
    java并发编程(十四)同步问题的内存可见性
    java并发编程(十三)经典问题生产者消费者问题
    java并发编程(十三)线程间通信中notifyAll造成的早期通知问题
    java并发编程(十一)线程间的通信notify通知的遗漏
  • 原文地址:https://www.cnblogs.com/dupang/p/7899902.html
Copyright © 2011-2022 走看看