数据结构 红黑树三

时间:2021-04-22
本文章向大家介绍数据结构 红黑树三,主要包括数据结构 红黑树三使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

删除操作

我们按照被删除的节点的子节点个数,分以下三种情况来讨论:

  1. 被删除节点没有孩子。只需要修改其父节点,用NIL去替换自己。
  2. 被删除节点有一个孩子。也只需要修改其父节点,用这个孩子去替换自己。
  3. 被删除节点有两个孩子。那么先找z的后继y(一定在z的右子树中),并让y占据树中z的位置。z的原来的右子树部分称为y的新的右子树,并且z的左子树成为y的左子树。

   前两种情况比较简单,至于第三种情况,我们还可以细分:

        a.如果z的后继y就是z的右孩子(即y没有左孩子),直接用y代替z,并保留y的右子树,如下图所示:

    b.如果z的后继y不是z的右孩子,先用y的右孩子替换y,再用y替换z。如下图所示:

算法描述

// 替换
RB_TRANSPLANT(T, u, v)
    if u.p == T.nil             // u是根节点
        T.root = v
    elseif u == u.p.left        // u是左孩子
        u.p.left = v
    else                        // u是右孩子
        u.p.right = v
    v.p = u.p                   // 更新父节点

备注:强调该替换算法只处理v的父节点,并没有考虑u,v子节点的情况,u,v子节点都需要自行处理


// 查找最小元素
TREE_MINIMUN(X)
    while x.left != T.nil
        x = x.left
    return x
    
// 查找最大元素
TREE_MAXIMUN(X)
    while x.right != T.nil
        x = x.right
    return x
    
    
    
// 删除
RB_DELETE(T, z)
    y = z
    y-original-color = y.color
    if z.left == T.nil                       // 删除节点z没有左孩子,直接用右孩子来替换自己(真.删除)
        x = z.right                          //
        RB_TRANSPLANT(T, z, z.right)
    elseif z.right == T.nil                  // 删除节点z没有右孩子,直接用左孩子来替换自己(真.删除)
        x = z.left
        RB_TRANSPLANT(T, z, z.left)
    else                                     // 删除节点z存在左孩子和右孩子
        y = TREE_MINIMUN(z.right)            // y赋值为删除节点z右子树最小节点(此时的y绝对没有左孩子)
        y-original-color = y.color           // 
        x = y.right                          // 
        if y.p != z                          // 右子树最小节点y不是z的孩子
            RB_TRANSPLANT(T, y, y.right)     // 用y的右孩子替换y本身(因为y会被用来替换z,相当于y也被删除了,需要处理原来y原来的一些孩子关系)(真.删除)
        else
            // 说明删除节点z的后继y就是z的右孩子,此时可以保持y的孩子节点,无需变动
        RB_TRANSPLANT(T, z, y)               // 用y替换掉z节点(y的孩子关系已经处理完成了)
        y.right = z.right                    // 处理删除节点z的右孩子关系
        y.right.p = y
        y.left = z.left                      // 处理删除节点z的右孩子关系
        y.left.p = y
        y.color = z.color                    // 着色新结点的颜色(假.删除,本质是替换,因此不会影响被替换节点的红黑性质)
    if y-original-color == BLACK             // 删除红色节点不会破坏红黑树的特性 ②
        RB_DELETE_FINXUP(T, x)               // 删除再平衡

备注:

① x表示的是发生了移动的节点,可能会破坏红黑树的性质

② y-original-color是记录被删除节点的颜色,如果颜色是红色,那么其子节点一定是黑色(被删除的节点都是被自己的子节点所取代),连续2个黑色并不违背红黑树的性质。如果颜色是黑色,那么子节点的颜色可能是红色,被删除节点的父节点也可能是红色,此时就会破坏红黑树的性质

③ 真删除表示节点被真实删除了,新替补上来的节点的颜色并没有被修改,假删除,替换的节点孩子关系被替换,并且颜色也被替换,对原节点亲属关系几乎没有改变

删除再平衡平衡算法描述

// 删除再平衡(心里默念x是双色)
RB_DELETE_FINXUP(T, x)
    while x != T.root and x.color == BLACK                                 // 关于x.color == RED情况说明见①
        if x == x.p.left                                                   // x是父节点左孩子
            w = x.p.right                                                  // w是父节点右孩子
            if w.color == RED                                              // 兄弟节点颜色是红色,说明父节点必须是黑色
                w.color = BLACK                                            // case1          
                x.p.color = RED                                            // case1
                LEFT_ROTATE(T, x.p)                                        // case1 注意此时x.p的右孩子已经更新
                w = x.p.right                                              // case1 w指向原先自己的左孩子
            if w.left.color == BLACK and w.right.color == BLACK            // w的孩子节点都是黑色 
                w.color = RED                                              // case2 修改w节点的颜色为红色
                x = x.p                                                    // case2 x指向其父节点
            elseif w.right.color == BLACK                                  // w的左孩子是红色,右孩子是黑色
                w.left.color == BLACK                                      // case3
                w.color = RED                                              // case3
                RIGHT_ROTATE(T, w)                                         // case3
                w = x.p.right                                              // case3 w指向原先自己的左孩子
            else
                w.color = x.p.color                                        // case4 w着色为父节点的颜色
                x.p.color = BLACK                                          // case4 设置x父节点为黑色
                w.right.color = BLACK                                      // case4 设置w右孩子的颜色为黑色
                LEFT_ROTATE(T, x.p)                                        // case4 左旋x的父节点
                x = T.root                                                 // case4
        else
            // same 
    x.color = BLACK                                                       // 如果x.color==RED 直接将x的颜色着色为BLACK

备注:

①:为啥不考虑x是红色的情况?

假设被真.删除的节点z是黑色(除非真.删除是根结点外)都将导致原先包含z结点的简单路径上的黑结点数少1,将违背性质3。修正这一问题的方式是我们将现在占据z结点位置的x结点"再涂上一层黑色"(②),当然,涂色操作并不反映在代码上,即我们不会修改x的color属性,我们只是"在心中记住",适当的时候会把x的这层黑色涂到某个红色结点上以达到目的。"涂了两层色"的x结点可能是双层黑色或红黑色,它们分别会"贡献"2或1个黑色结点数。

这句话很难理解,意思是现在黑高肯定少了1,那么就需要增加一个黑色节点,既然是x替换了被删除的节点,那么这个黑色就被着色在x上,这样x会有两种颜色(一种他本身自带的颜色,一种黑色,黑色是被删除节点赋予x的),这样可以保证原先包含z结点的简单路径黑高保持不变

③ 关于当一条路径上一个节点被删除,导致高度下降问题分析

红黑树的高度是以黑高来确认的,我承认事实上树高下降了,但是通过着色黑高并没有下降,黑高的不下降对于未来的插入操作将会变得简单,这也可以说明红黑树并非完全平衡。

④ RB_DELETE-FIXUP修正的目的就是要x路径上黑高+1(因为真删除黑色节点会导致黑高-1)

分析RB_DELETE-FIXUP修正过程

Case 1:x的右兄弟w是红色,说明x的父结点一定是黑色。

所作的操作是:交换w和其父结点的颜色,即把w换为黑色,其父结点换位红色;然后对父结点左旋,w重新指向x的右兄弟(该结点原本是w的左孩子,所以一定为黑色),黑高保持不变。

有什么影响:将x的兄弟节点变成黑色

Case 2:x的兄弟节点w是黑色的,并且w的两个子节点也是黑色的。

所作的操作是:将w换为红色,x指向其父结点,w路径上黑高-1。

有什么影响:将x本身的黑色着色给父节点来实现x路径上黑高+1,并且w路径黑高+1这个目的,这个目的的实现与父节点的颜色密切相关,父节点是红色,那么会退出循环(循环体外实现给父节点着色为黑色的功能),父节点着色黑色实现了黑高+1这个目的,如果父节点为黑色,那么只能x上跳一级继续循环,期待后面可以实现黑高+1这个目的

Case 3:x的兄弟节点w是黑色的,并且w的左孩子为红色,右孩子为黑色。

所作的操作是:着色w的左孩子为黑色,着色w本身为红色,对w进行右旋操作,黑高保持不变。

有什么影响:case3是将w的右孩子变成红色,目的是为了构造case4

Case 4:x的兄弟节点w是黑色的,并且w的右孩子为红色,左孩子颜色任意。

所作的操作是:w着色为父节点的颜色,设置x父节点为黑色,设置w右孩子的颜色为黑色,左旋x的父节点,退出循环。

有什么影响:case4实现了x路径上黑高+1

case说明:

1.case2,case3,case4的w节点一定是黑色,因为case1可以确保这一点

2.观察所有case,发现只有case2和case4可以实现x路径黑高+1这个目的

详细分析流程如下图

/**********case1+case2*******************/
/*********case1+case3+case4************************/
/***********case2***************************/
/*******case2*********************/

原文地址:https://www.cnblogs.com/zhanggaofeng/p/14691562.html