zoukankan      html  css  js  c++  java
  • 深入理解红黑树

      红黑树是平衡树的一种,保证最坏情况下操作时间复杂度为O(lgo(n))。红黑树的应用比较广泛,比如作为C++中STL的set和map的底层数据结构,Java集合中TreeSet和TreeMap的底层数据结构等。学习红黑树,可以把二叉查找树作为参考,这样有助于加深理解。红黑树的操作主要包括节点旋转、插入、删除等操作,下面咱们就一一来看:

    1、红黑树性质

    1. 每个节点是红色的,或者是黑色的
    2. 根节点是黑色的
    3. 每个叶节点(nil)是黑色的
    4. 如果一个节点是红色的,则它的两个子节点都是黑色的
    5. 对每个节点,从该节点到其后代叶节点的简单路径上,均包含相同数目的黑色节点

    红黑树整体节点图示如下:

    2、旋转

      旋转是保持二叉搜索树(红黑树)局部性质的操作,包括左旋和右旋。当在节点x上做左旋时,假设它的右孩子为y而不是T.nil,X可为其右孩子不是T.nil的任何节点,同理,X做右旋也是一样的。

    伪代码如下(左旋)

    左旋Java示例代码:

    /**
     * 左旋操作
     */
    private void leftRotate(Node x) {
        Node y = x.right;
    
        x.right = y.left;
        if (y.left != nil) {
            y.left.parent = x;
        }
        y.parent = x.parent;
        if (x.parent == nil) {
            root = y;
        } else if (x == x.parent.left) {
            x.parent.left = y;
        } else {
            x.parent.right = y;
        }
        
        x.parent = y;
        y.left = x;
    }

    3、插入

      在O(log(n))时间内插入一个节点,RB-INSERT过程完成该操作,像一个普通的二叉搜索树插入操作一样,然后将要插入的节点Z着为红色。为了保持红黑树的性质,调用一个辅助函数RB-INSERT-FIXUP来对节点重新着色并旋转。待插入节点Z已保存了数据项。

    RB-INSERT-FIXUP过程伪代码


      注意:Case1属于if判断条件,Case2和Case3属于else语句,Case2是else语句中的if判断语句的。具体可以看代码。

    插入过程Java代码示例:

    /**
     * 往红黑树中插入一个元素
     * @param data
     */
    public void insert(int data) {
        Node y = nil;
        Node x = root;
        
        if (root == nil) {
            root = newNode(data);
            root.color = Node.BLACK;
        } else {
            while (x != nil) {
                if (data < x.data) {
                    y = x;
                    x = x.left;
                } else if (data > x.data) {
                    y = x;
                    x = x.right;
                } else {
                    return;
                }
            }
            
            Node z = newNode(data);
            z.parent = y;
            if (data < y.data) {
                y.left = z;
            } else {
                y.right = z;
            }
            if (y.color == Node.RED) {
                insertFixup(z);
            }
        }
    }

    RB-INSERT-FIXUP过程分析

      第1-15行的while循环在每次迭代的开始始终保持以下3个条件是不变的

    1. 节点z是红节点
    2. 如果z.p是根节点,则z.p是黑节点
    3. 如果有任何红黑树性质贝破坏,则至多有一条被破坏,或是性质2,或是性质4(各个性质见红黑树性质总结)。如果性质2被破坏,则因为z是根节点且是红节点。性质4被破坏则是因为z和z.p都是红节点。

    循环终止条件:

      循环终止因为z.p是黑色的(如果z是根节点,则z.p是黑色哨兵节点),这样,在循环终止时并没有违反性质4,唯一可能不成立的就是性质2,不过第16行(伪代码中的行)恢复了这个性质。

    循环保持条件:

      循环保持有6中条件,其中3种和另外3种是对称的,这取决于z的父节点z.p是z的祖父节点z.p.p的左孩子还是右孩子。情况1、2、3的区别就在于z 的父节点的兄弟节点(z的叔节点)的颜色不同,加入y指向z的叔节点,如果y的颜色是红色的,则执行情况1,否则转向情况2和3。在所有的3种情况中,z 的祖父节点z.p.p是黑色的,因为他的父节点z.p是红色的,故性质4只有在z和z.p之间被破坏了。

    情况1:z的叔节点y是红色的

           此时对应Case1,将z.p和y都着成黑色,解决z和z.p都是红色的问题,将z.p.p着成红色保持性质5,然后把z.p.p作为新节点z来继续进行while循环,指针z上移两层

    情况2:z的叔节点y是黑色的且z是一个右孩子

    情况3:z的叔节点y是黑色的且z是一个左孩子

           两种情况中,y都是黑色的,通过z是左孩子还是右孩子来区分,情况2中可以通过一个左旋来转化为情况3,此时z为左孩子。因为z和z.p都是红节点,对黑 高(从某个节点出发,不含该节点,到达一个叶节点的任意一条简答路径上的黑色节点数为该节点的黑高)和性质5都无影响。在情况3中,改变某些节点颜色并做 一次右旋,保持性质5,这样不在有两个红色节点相邻,处理完毕,此时z.p是黑色的,所以无需再执行while循环了。

    RB-INSERT-FIXUP的Java代码实现

    /**
     * 插入节点后不满足红黑树条件时来修复
     * @param z 待插入的节点
     */
    private void insertFixup(Node z) {
        // y为z节点的叔叔节点
        Node y = null;
        
        while (z.parent.color == Node.RED) {
            if (z.parent == z.parent.parent.left) {
                y = z.parent.parent.right;
                if (y.color == Node.RED) {
                    z.parent.color = Node.BLACK;
                    y.color = Node.BLACK;
                    z.parent.parent.color = Node.RED;
                    z = z.parent.parent;
                } else {
                    if (z == z.parent.right) {
                        z = z.parent;
                        leftRotate(z);
                    }
                    
                    z.parent.color = Node.BLACK;
                    z.parent.parent.color = Node.RED;
                    rightRotate(z.parent.parent);
                }
            } else {
                y = z.parent.parent.left;
                if (y.color == Node.RED) {
                    z.parent.color = Node.BLACK;
                    y.color = Node.BLACK;
                    z.parent.parent.color = Node.RED;
                    z = z.parent.parent;
                } else {
                    if (z == z.parent.left) {
                        z = z.parent;
                        rightRotate(z);
                    }
                    
                    z.parent.color = Node.BLACK;
                    z.parent.parent.color = Node.RED;
                    leftRotate(z.parent.parent);
                }
            }
        }
        
        root.color = Node.BLACK;
    }

    4、删除

      删除操作与插入操作相比,略显复杂,与插入操作一样,也要花费O(log(n))时间。从红黑树中删除节点,需设计一个供TREE-DELETE调用的子过程TRANSPLANT,并应用到红黑树中,TRANSPLANT过程用来调整两个节点的关系,其中的一个节点要替换掉另一个节点

    TRANSPLANT过程

      u节点表示将要被替换掉的节点,v节点是准备替换u节点的

    RB-TRANSPLANT的Java代码实现

    /**
     * 两个节点的替换,newNode替换oldNode
     * @param oldNode
     * @param newNode
     */
    private void transplant(Node oldNode, Node newNode) {
        if (oldNode.parent == nil) {
            root = newNode;
        } else if (oldNode == oldNode.parent.left) {
            oldNode.parent.left = newNode;
        } else {
            oldNode.parent.right = newNode;
        }
        
        newNode.parent = oldNode.parent;
    }

    RB-DELETE过程

      RB-DELETE中,z节点是要被删除的节点,其中记录了节点y的踪迹,y有可能导致红黑树性质破坏,当想删除节点z,且z的子节点少于2个时,z从书中删除,并让y称为z。当z有两个子节点时,y应该是z的后继,并且将y移到z的位置。在节点被删除或者移动时,必须记住y的颜色,并且记录节点x的踪迹,将x移到y的原来的位置,因为节点x可能引起红黑树性质破坏。删除节点z后,RB-DELETE-FIXUP过程通过改变颜色和执行旋转来恢复红黑树性质。

      我们保存节点x的踪迹,使它移至节点y的原来位置。第4、7和11行的赋值语句令x或指向y的唯一子节点或指向y哨兵T.nil(y没有子节点的话)。  第5、8或14行调用RB-TRANSPLANT时,传递的第2个参数与x相同

      如果y是黑色的,则有可能引入一个或多个破坏红黑色的情况,所以在第22行调用RB-TRANSPLANT来恢复红黑树性质。如果y是红色的,当y被删除或移动时,红黑树性质依然成立,因为(1) 树中黑高没有变化 (2) 不存在两个相邻的红节点,因为y在树中占据了z的位置,z的位置肯定不会违反红黑树性质的。另外,如果y是z的右孩子,则y的原右孩子x代替y,如果y是红色的,则x一定是黑色的,一次用x替代y不会使两个红节点相邻。 (3) 如果y是红色的,就不会是根节点,所以根节点仍然是黑色的。

    y为黑节点情况分析

    RB-DELETE的Java代码实现

    /**
     * 从红黑树中移除一个元素
     * @param data 待移除的元素
     */
    public void remove(int data) {
        Node z = contains(data);
        if (z == null) {
            return;
        }
        
        Node x;
        Node y = z;
        int yOldColor = y.color;
        if (z.left == nil) {
            x = z.right;
            transplant(z, x);
        } else if (z.right == nil) {
            x = z.left;
            transplant(z,  x);
        } else {
            y = z.right;
            while (y.left != nil) {
                y = y.left;
            }
            yOldColor = y.color;
            x = y.right;
            
            if (y.parent == z) {
                x.parent = y;
            } else {
                transplant(y, y.right);
                y.right = z.right;
                y.right.right = y;
            }
            
            transplant(z, y);
            y.left = z.left;
            y.left.parent = y;
            y.color = z.color;
        }
        
        if (yOldColor == Node.BLACK) {
            removeFixup(x);
        }
    }

    RB-DELETE-FIXUP过程

    ps:图片上的情况层次关系对应不是很齐,可以看码来的清楚

    4种情况的转换关系

    • 1 -> 2、3、4
    • 2 -> 1、2、3、4、修复 (只有Case2是节点上移,所有有可能会使x=root)
    • 3 -> 4
    • 4 -> 修复

      无论哪个Case,要么是为了到达平衡两个节点,路径上黑色数目相同的目的,要么是通过变形间接达到这个目的。

    while循环的目标是将额外的黑色沿树上移,直到:

    1. x指向红黑节点,此时在第23行处将x着为(单个)黑色
    2. x指向根节点,此时可以简单地移除额外的黑色
    3. 执行适当的旋转和重新着色

    代码中的4种情况图示


           上图给出了代码中出现的四种情况,再具体研究每一个情况时,先看看如何证实每种情况中的变换保证性质5。关键思想是在每种情况下,从子树的跟(包括跟)到每棵子树之间的黑色节点个数(包括x的额外黑色)并不被变换改变,因此,如果性质5在变换之前成立,则在变换之后也成立。比如:在情况1中,在变换前后,根节点到子树a或b之间的黑色节点数是3(因为x增加了一层黑色),类似的,在变换前后根节点到其余的4个子树的叶节点中的任何一个的黑节点数是2。在图13-7(b)中,计数是还要包括所示子树的根节点的color属性的值,它是RED或是BLACK,如果定义count(RED)=0以及count(BLACK)=1,那么变换前后根节点至a的黑节点都为2+count(c)。在此情况下,变换之后新节点x具有color属性的c,但是这个节点的颜色是红黑(如果c=RED)或者双重黑色的(如果c=BLACK)。其他情况可以类似加以验证。

    情况1:x的兄弟节点w是红色的

      情况1(见RB-DELETE-FIXUP的第5-8行和图13-7(a))发生在节点x的兄弟节点w为红色时。因为w必须有黑色子节点,所有可以改变w的x.p的颜色,然后对x.p做一次左旋而不违反红黑树的任何性质。现在x的新兄弟节点是旋转之前w的某个子节点,其颜色为黑色。这样,就将情况1转变为了情况2、3或者4。

      当节点w为黑色时,属于情况2、3或者4;这些情况有w的子节点颜色来区分了。

    情况2:x的兄弟节点w是黑色的,而且w的两个子节点都是黑色的

      在情况2(见RB-DELETE-FIXUP的第10-11行和图13-7(b)),w的两个子节点都是黑色的,因为w也是黑色的,所以从x和w上去掉一重黑色,使得x只有一重黑色而w为红色。为了补偿从x和w中去掉的一重黑色,在原来是红色或黑色的x.p上增加一重额外的黑色。通过将x.p作为新节点x来重复while循环。注意到,如果通过情况1进入到情况2,则新节点x就是红黑色的,因为原来的x.p是红色的,因此,新节点x的color属性值为RED,并且在测试循环条件后循环终止。然后,在第23行处将新节点x着为(单一)黑色。

    情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的

      情况3(见RB-DELETE-FIXUP的第13-16行和图13-7(c))发生在w为黑色且其左孩子为红色,右孩子为黑色时。可以交换w和其左孩子w.left的颜色,然后对w进行右旋而不违反红黑树的性质。现在x的新节点w是一个有红色右孩子的黑色节点,这样我们就从情况3转换成了情况4。

    情况4:x的兄弟节点w是黑色的,且w的右孩子是红色的

      情况4(见RB-DELETE-FIXUP的第17-21行和图13-7(d))发生在节点x的兄弟节点w为黑色且w的右孩子为红色时,通过进行某些颜色修改并对x.p做一次左旋,可以去掉x的额外黑色,从而使它变为单重黑色,而且不破坏红黑树性质。将x设为设为根root后,当while循环测试其循环条件时,循环终止。

    RB-DELETE-FIXUP的Java代码实现

    /**
     * 移除一个元素后红黑树不符合要求时的修复方法
     * @param x
     */
    private void removeFixup(Node x) {
        Node y;
        
        while (x != root && x.color == Node.BLACK) {
            if (x == x.parent.left) {
                y = x.parent.right;
                if (y.color == Node.RED) { //情况1:x的兄弟节点w是红色
                    y.color = Node.BLACK;
                    x.parent.color = Node.RED;
                    leftRotate(x.parent);
                    y = x.parent.right;
                }
                
                if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { //情况2:x的兄弟节点w是黑色,而且w的两个子节点都是黑色的
                    y.color = Node.RED;
                    x = x.parent;
                } else {
                    if (y.right.color == Node.BLACK) { //情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的
                        y.left.color = Node.BLACK;
                        y.color = Node.RED;
                        rightRotate(y);
                        y = x.parent.right;
                    }
                    
                    y.color = x.parent.color; //情况4:x的兄弟节点w是黑色的,且w的右孩子是红色的
                    x.parent.color = Node.BLACK;
                    y.right.color = Node.BLACK;
                    leftRotate(x.parent);
                    x = root;
                }
            } else {
                y = x.parent.left;
                if (y.color == Node.RED) {
                    y.color = Node.BLACK;
                    x.parent.color = Node.RED;
                    rightRotate(x.parent);
                    y = x.parent.left;
                }
                
                if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) {
                    y.color = Node.RED;
                    x = x.parent;
                } else {
                    if (y.left.color == Node.BLACK) {
                        y.right.color = Node.BLACK;
                        y.color = Node.RED;
                        leftRotate(y);
                        y = x.parent.left;
                    }
                    
                    y.color = x.parent.color;
                    x.parent.color = Node.BLACK;
                    y.left.color = Node.BLACK;
                    rightRotate(x.parent);
                    x = root;
                }
            }
        }
        x.color = Node.BLACK;
    }

    删除分析:

    5、完整程序Java代码实现

    红黑树Rbtre.java文件:

      1 package tree;
      2 
      3 /**
      4  * 红黑树节点类
      5  */
      6 class Node {
      7     // 红黑节点颜色
      8     public final static int RED = 0;
      9     public final static int BLACK = 1;
     10     
     11     // ------------------------------ Instance Variables
     12     
     13     public Node left;    // 左节点
     14     public Node right;    // 右节点
     15     public Node parent; // 父节点
     16     public int color;    // 节点颜色
     17     public int data;    // 数据域,int型数据
     18 }
     19 
     20 /**
     21  * 红黑树类
     22  * 
     23  */
     24 public class Rbtree {
     25     
     26     // ------------------------------ Instance Variables
     27     
     28     // 红黑树空节点
     29     public Node nil;
     30     
     31     // 红黑树根节点
     32     public Node root;
     33 
     34     // ------------------------------ Constructors
     35     
     36     /**
     37      * Init nil node and root node.
     38      */
     39     public Rbtree() {
     40         nil = new Node();
     41         nil.left = nil.right = nil.parent = nil;
     42         nil.color = Node.BLACK;
     43         root = nil;
     44     }
     45     
     46     // ------------------------------ Public methods
     47     
     48     /**
     49      * 往红黑树中插入一个元素
     50      * @param data
     51      */
     52     public void insert(int data) {
     53         Node y = nil;
     54         Node x = root;
     55         
     56         if (root == nil) {
     57             root = newNode(data);
     58             root.color = Node.BLACK;
     59         } else {
     60             while (x != nil) {
     61                 if (data < x.data) {
     62                     y = x;
     63                     x = x.left;
     64                 } else if (data > x.data) {
     65                     y = x;
     66                     x = x.right;
     67                 } else {
     68                     return;
     69                 }
     70             }
     71             
     72             Node z = newNode(data);
     73             z.parent = y;
     74             if (data < y.data) {
     75                 y.left = z;
     76             } else {
     77                 y.right = z;
     78             }
     79             if (y.color == Node.RED) {
     80                 insertFixup(z);
     81             }
     82         }
     83     }
     84     
     85     /**
     86      * 从红黑树中移除一个元素
     87      * @param data 待移除的元素
     88      */
     89     public void remove(int data) {
     90         Node z = contains(data);
     91         if (z == null) {
     92             return;
     93         }
     94         
     95         Node x;
     96         Node y = z;
     97         int yOldColor = y.color;
     98         if (z.left == nil) {
     99             x = z.right;
    100             transplant(z, x);
    101         } else if (z.right == nil) {
    102             x = z.left;
    103             transplant(z,  x);
    104         } else {
    105             y = z.right;
    106             while (y.left != nil) {
    107                 y = y.left;
    108             }
    109             yOldColor = y.color;
    110             x = y.right;
    111             
    112             if (y.parent == z) {
    113                 x.parent = y;
    114             } else {
    115                 transplant(y, y.right);
    116                 y.right = z.right;
    117                 y.right.right = y;
    118             }
    119             
    120             transplant(z, y);
    121             y.left = z.left;
    122             y.left.parent = y;
    123             y.color = z.color;
    124         }
    125         
    126         if (yOldColor == Node.BLACK) {
    127             removeFixup(x);
    128         }
    129     }
    130     
    131     /**
    132      * 红黑树中是否包含data
    133      * @param data
    134      * @return
    135      */
    136     public Node contains(int data) {
    137         Node node = root;
    138         
    139         while (node != null) {
    140             if (data < node.data) {
    141                 node = node.left;
    142             } else if (data > node.data) {
    143                 node = node.right;
    144             } else {
    145                 return node;
    146             }
    147         }
    148         return null;
    149     }
    150     
    151     @Override
    152     public String toString() {
    153         StringBuilder buf = new StringBuilder();
    154         
    155         if (root != nil) {
    156             toStringInternal(root, buf);
    157         }
    158         return buf.toString();
    159     }
    160     
    161     // ------------------------------ Private methods
    162     
    163     /**
    164      * New a initialize node, default node's color is RED
    165      */
    166     private Node newNode(int data) {
    167         Node node = new Node();
    168         
    169         node.data = data;
    170         node.color = Node.RED;
    171         node.left = node.right = node.parent = nil;
    172         return node;
    173     }
    174     
    175     private void toStringInternal(Node node, StringBuilder buf) {
    176         if (node != nil) {
    177             toStringInternal(node.left, buf);
    178             buf.append(node.data + (node.color == Node.RED ? "-red " : "-black "));
    179             toStringInternal(node.right, buf);
    180         }
    181     }
    182     
    183     /**
    184      * 两个节点的替换,newNode替换oldNode
    185      * @param oldNode
    186      * @param newNode
    187      */
    188     private void transplant(Node oldNode, Node newNode) {
    189         if (oldNode.parent == nil) {
    190             root = newNode;
    191         } else if (oldNode == oldNode.parent.left) {
    192             oldNode.parent.left = newNode;
    193         } else {
    194             oldNode.parent.right = newNode;
    195         }
    196         
    197         newNode.parent = oldNode.parent;
    198     }
    199     
    200     /**
    201      * 左旋操作
    202      */
    203     private void leftRotate(Node x) {
    204         Node y = x.right;
    205 
    206         x.right = y.left;
    207         if (y.left != nil) {
    208             y.left.parent = x;
    209         }
    210         y.parent = x.parent;
    211         if (x.parent == nil) {
    212             root = y;
    213         } else if (x == x.parent.left) {
    214             x.parent.left = y;
    215         } else {
    216             x.parent.right = y;
    217         }
    218         
    219         x.parent = y;
    220         y.left = x;
    221     }
    222     
    223     /**
    224      * 右旋操作
    225      */
    226     private void rightRotate(Node x) {
    227         Node y = x.left;
    228         
    229         x.left = y.right;
    230         if (y.right != nil) {
    231             y.right.parent = x;
    232         }
    233         y.parent = x.parent;
    234         if (x.parent == nil) {
    235             root = y;
    236         } else if (x == x.parent.left) {
    237             x.parent.left = y;
    238         } else {
    239             x.parent.right = y;
    240         }
    241         
    242         x.parent = y.parent;
    243         y.right = x;
    244     }
    245     
    246     /**
    247      * 插入节点后不满足红黑树条件时来修复
    248      * @param z 待插入的节点
    249      */
    250     private void insertFixup(Node z) {
    251         // y为z节点的叔叔节点
    252         Node y = null;
    253         
    254         while (z.parent.color == Node.RED) {
    255             if (z.parent == z.parent.parent.left) {
    256                 y = z.parent.parent.right;
    257                 if (y.color == Node.RED) { // Case1: x uncle node y is red
    258                     z.parent.color = Node.BLACK;
    259                     y.color = Node.BLACK;
    260                     z.parent.parent.color = Node.RED;
    261                 } else {
    262                     if (z == z.parent.right) { // Case2: x uncle node y is black, but x is right node
    263                         z = z.parent;
    264                         leftRotate(z);
    265                     }
    266                     
    267                     z.parent.color = Node.BLACK; // Case3: x uncle node y is black, but x is left node
    268                     z.parent.parent.color = Node.RED;
    269                     rightRotate(z.parent.parent);
    270                 }
    271             } else {
    272                 y = z.parent.parent.left;
    273                 if (y.color == Node.RED) {
    274                     z.parent.color = Node.BLACK;
    275                     y.color = Node.BLACK;
    276                     z.parent.parent.color = Node.RED;
    277                 } else {
    278                     if (z == z.parent.left) {
    279                         z = z.parent;
    280                         rightRotate(z);
    281                     }
    282                     
    283                     z.parent.color = Node.BLACK;
    284                     z.parent.parent.color = Node.RED;
    285                     leftRotate(z.parent.parent);
    286                 }
    287             }
    288         }
    289         
    290         root.color = Node.BLACK;
    291     }
    292     
    293     /**
    294      * 移除一个元素后红黑树不符合要求时的修复方法
    295      * @param x
    296      */
    297     private void removeFixup(Node x) {
    298         Node y;
    299         
    300         while (x != root && x.color == Node.BLACK) {
    301             if (x == x.parent.left) {
    302                 y = x.parent.right;
    303                 if (y.color == Node.RED) { //情况1:x的兄弟节点w是红色
    304                     y.color = Node.BLACK;
    305                     x.parent.color = Node.RED;
    306                     leftRotate(x.parent);
    307                     y = x.parent.right;
    308                 }
    309                 
    310                 if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) { //情况2:x的兄弟节点w是黑色,而且w的两个子节点都是黑色的
    311                     y.color = Node.RED;
    312                     x = x.parent;
    313                 } else {
    314                     if (y.right.color == Node.BLACK) { //情况3:x的兄弟节点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的
    315                         y.left.color = Node.BLACK;
    316                         y.color = Node.RED;
    317                         rightRotate(y);
    318                         y = x.parent.right;
    319                     }
    320                     
    321                     y.color = x.parent.color; //情况4:x的兄弟节点w是黑色的,且w的右孩子是红色的
    322                     x.parent.color = Node.BLACK;
    323                     y.right.color = Node.BLACK;
    324                     leftRotate(x.parent);
    325                     x = root;
    326                 }
    327             } else {
    328                 y = x.parent.left;
    329                 if (y.color == Node.RED) {
    330                     y.color = Node.BLACK;
    331                     x.parent.color = Node.RED;
    332                     rightRotate(x.parent);
    333                     y = x.parent.left;
    334                 }
    335                 
    336                 if (y.left.color == Node.BLACK && y.right.color == Node.BLACK) {
    337                     y.color = Node.RED;
    338                     x = x.parent;
    339                 } else {
    340                     if (y.left.color == Node.BLACK) {
    341                         y.right.color = Node.BLACK;
    342                         y.color = Node.RED;
    343                         leftRotate(y);
    344                         y = x.parent.left;
    345                     }
    346                     
    347                     y.color = x.parent.color;
    348                     x.parent.color = Node.BLACK;
    349                     y.left.color = Node.BLACK;
    350                     rightRotate(x.parent);
    351                     x = root;
    352                 }
    353             }
    354         }
    355         x.color = Node.BLACK;
    356     }
    357 }
    View Code

    测试用例程序Main.java文件

     1 package tree;
     2 
     3 public class Main {
     4     public static void main(String[] args) {
     5         Rbtree tree = new Rbtree();
     6         
     7         tree.insert(12);
     8         tree.insert(12);
     9         tree.insert(2);
    10         tree.insert(23);
    11         tree.insert(1);
    12         
    13         System.out.println(tree);
    14         
    15         tree.remove(1);
    16         tree.remove(2);
    17         tree.remove(12);
    18         tree.remove(12);
    19         tree.remove(23);
    20         System.out.println(tree);
    21     }
    22 }
    View Code

    参考资料:

      1、《算法导论》第13章 红黑树

      2、红黑树理解 - 数据结构

      3、更多数据结构实现代码(C++版)

  • 相关阅读:
    P3381 【模板】最小费用最大流
    Android Studio安装插件提示was not installed: Cannot download的解决
    webpack 引用vconsole
    vue ...mapMutations 的第一个参数默认为 数据对象state
    vue 如何实现在函数中触发路由跳转
    axios 使用
    rem 的使用
    vscode 习惯配置
    修改默认的inout输入框背景颜色
    vue -本地服务配置
  • 原文地址:https://www.cnblogs.com/luoxn28/p/5540638.html
Copyright © 2011-2022 走看看