红黑树删除操作详细分析

332 阅读6分钟

本文是对《算法导论》第三版第十三章红黑树删除操作的解读。书中用一个结点有两种复合颜色来分析,但是它的伪代码中并没有体现这个概念,读起来让人费解,因此本文不使用书中的解释,而是参考书中伪代码和图例,用更直白的语言来描述整个过程。

基本情况

下面是第183页RB-DELETE(T,z)伪代码的解读,删除操作会分为以下3种情况:

  1. 被删的z左子结点为空,则右子结点顶上z的位置。(对应伪代码第3-5行)

    1. 如果z是红色,则结束。
    2. 如果z是黑色,则分析右子结点(z.right),书中为x。
      1. 因为删了z会导致路径上少了一个黑色结点,所以右子树(z.right)要补上一个黑色结点,让红黑树重新达到完美平衡。
      2. 如果x是红色的,直接把它变成黑色,相当于增加一个黑色结点,直接不走RB-DELETE-FIXUP的while循环把x设置为黑色,结束。
      3. 如果x是黑色的,通过变换从x的兄弟结点调一个黑色的过来,请看下面的RB-DELETE-FIXUP。
  2. 被删的z右子结点为空,则左子结点顶上z的位置。(对应伪代码第6-8行)

    1. 如果z是红色,则结束。

    2. 如果z是黑色,则分析左子结点(z.left),书中为x。

      1. 因为删了z会导致路径上少了一个黑色结点,所以左子树(z.left)要补上一个黑色结点,让红黑树重新达到完美平衡。
      2. 如果x是红色的,直接把它变成黑色,相当于增加一个黑色结点,直接不走RB-DELETE-FIXUP的while循环把x设置为黑色,结束。
      3. 如果x是黑色的,通过变换从x的兄弟结点调一个黑色的过来,请看下面的RB-DELETE-FIXUP。
  3. 从z的右子树找出最小值y,来替代z。(也可以用z的左子树的最大值替代z,都行)因为y是z右子树的最小值,则y必定没有左子结点。(对应伪代码第9-11行)

    1. 如果y是z的右子结点。(对应伪代码第12、13、17-20行)

      • y顶替z的位置,y继承z的颜色。y的右子结点x顶替y的位置。

        1. 如果y原来的颜色是红色,那z是黑色的,x也是黑色的,因为不能有连续红色。y继承z的颜色,也就是少了原来y的红色,不影响,结束。
        2. 如果y原来的颜色是黑色,替换z之后会出现4中情况,1y黑x黑、2y红x黑、3y黑x红、4y红x红,路径上都会少一个黑色结点通过变换从x的兄弟结点调一个黑色的过来,请看下面的RB-DELETE-FIXUP。
    2. 如果y不是z的右子结点,而是更下面的结点。(对应伪代码第14-20行)

      1. y继承z的颜色。如果y原来是红色,那不管z原来什么颜色,路径上只会少了y原来的红色,所以不影响,结束。
      2. y继承z的颜色。如果y原来是黑色,路径上就会少一个黑色结点通过变换从x的兄弟结点调一个黑色的过来,请看下面的RB-DELETE-FIXUP。

RB-DELETE-FIXUP

至此,我们已经解决大部分问题,只剩下因为路径上少一个黑色结点,怎样通过变换从x的兄弟结点调一个黑色的过来这个问题,即P185的RB-DELETE-FIXUP伪代码要解决的问题,但这一段伪代码不完整,最好从官网下载Python Code来对照阅读。

下面是解决这个问题要采取的步骤:(下面的情况对应P186图13-7)

  1. 把红色的sibling(w)变成黑色。(对应情况1)

  2. sibling(w)已经变成黑色或原来就是黑色,分4种情况(第三第四种情况合并为1种情况)

    1. w左右结点都是黑色(对应情况2)

      • 把w变红,这样就把从x的sibling中挪一个黑色结点变成从x.parent的sibling中去挪结点。这时会重新进入循环,重新从步骤1开始执行。
    2. w左结点红、右结点黑(对应情况3)

      1. 把w的右子结点变成红色,进入下面这种情况。
    3. w右结点红、左结点红或黑(红黑都无所谓)(对应情况4)

      1. 通过旋转变色,给x路新增黑色结点,结束。

除了2.1.这个步骤,其他情况都要执行到达2.3.即情况4才能结束。2.1.(对应情况2)这个步骤稍微复杂一点,看下面详细分析。(下面的情况对应P186图13-7)

  • 情况1

    • 执行转换动作。虽然没有改变路径上黑色的数量,但是转换成了其他能够做出添加黑色结点的情况。
    • 把红色的sibling变成黑色。则x和sibling都是黑色。情况234都符合x和sibling都是黑色。
  • 情况2

    • 原本是A这条路少一个黑色结点,现在把D变成红色,则B下面的两条路都少一个黑色结点,那么就把原本A路少一个黑色结点转变成B路少一个黑色结点,即要给A路增加一个黑色结点转变成给B路增加一个黑色结点。

    • B结点可红可黑

      • 红色时,转换之后的新x因为是红色,会退出循环,直接把x设为黑色,相当于增加一个黑色结点,结束。
      • 黑色时,如果循环后碰到情况34,就按情况34的步骤走。如果x一直上移到root的过程中都没有碰到情况34(x上移指的是从原来A是x变成B是x,看图13-7),因为每次上移都会把新的x的sibling变红色,意味着整棵树的叶子结点(NIL)到root的距离比执行删除操作前减1。也就是这里不是从其他地方挪一个黑色结点过来以维持红黑树平衡,而是从其他地方减少一个黑色结点来维持红黑树平衡。
  • 情况3

    • 执行转换动作。虽然没有改变路径上黑色的数量,但是转换成了其他能够做出添加黑色结点的情况。(同case1)
    • 目的:把w的右子结点变成红色。
  • 情况4

    • 通过其他的情况转换到本情况,执行新增一个黑色结点的操作。

以上就是红黑树的删除操作,还是看不懂的话,可以先看完P166二叉搜索树的删除操作,再看红黑树的删除操作,这样会更加清晰一些。