红黑树:Kahrs 版本

8

我正在尝试理解Okasaki的红黑树实现以及Kahrs的删除方法(未类型化版本)。

在删除实现中,使用了一个名为app的函数,它似乎是在“合并”要删除的节点的子节点。而且,算法似乎仍然使用相同的方法“破坏”红-红属性而不是黑高度(如果我错了,请纠正我)。我们总是创建红色节点(即使我们打破了红-红属性)。沿着要删除的节点为根的子树向下遍历,纠正红-红违规,一旦到达叶子节点,我们开始沿着路径向上走(从“新树”合并开始),修复沿着路径的红-红违规。

app :: RB a -> RB a -> RB a
app E x = x
app x E = x
app (T R a x b) (T R c y d) =
    case app b c of
        T R b' z c' -> T R(T R a x b') z (T R c' y d)
        bc -> T R a x (T R bc y d)
app (T B a x b) (T B c y d) = 
    case app b c of
        T R b' z c' -> T R(T B a x b') z (T B c' y d)
        bc -> balleft a x (T B bc y d)
app a (T R b x c) = T R (app a b) x c
app (T R a x b) c = T R a x (app b c)
  1. 我无法理解我们如何在“不创建”/“修复”黑色高度违规方面做出贡献?删除一个黑色节点会在路径上某个节点处创建bh-1bh子树。
  2. 这篇论文的结果表明,这种实现方式非常快,并且“合并”方法可能是回答速度增加的关键。

任何关于这个“合并”操作的说明都将是很好的。请注意,这不是作业问题或其他什么。我正在独立地研究Okasaki中给出的实现,并填写“杂乱”的删除。

谢谢。


“merge”方法可能是关键:我的意思是,论文中的结果似乎表明,“与其使用标准的替换要删除的节点为中序继承者,不如将树合并在一起”的方法可能是速度快的原因。 - user3169543
1个回答

5
鉴于此主题可以说很多,我会尽可能地按照您的问题进行回答,但如果我漏掉了重要的内容,请告诉我。
这个`app`到底是干什么的?
您是正确的,app会打破红色不变性而不是黑色不变性,并在返回时修复它。
它将符合顺序属性、黑色不变性、红色不变性且具有相同黑深度的两个子树附加或合并成一个新树,该新树也满足顺序属性、黑色不变性和红色不变性。唯一的例外是根节点或app rb1 rb2有时是红色的且有两个红色子树。这些树被称为“红外线”。在delete中,通过设置这行代码中的根节点为黑色来处理这种情况。
 case del t of {T _ a y b -> T B a y b; _ -> E}

声明1(顺序属性)如果输入的rb1rb2分别满足顺序属性(左子树 < 节点值 < 右子树),并且rb1中的最大值小于rb2中的最小值,则app rb1 rb2也满足顺序属性。

这个很容易证明。实际上,当查看代码时,您甚至可以看到它——a始终保持在bb'的左侧,后者始终保持在cc'的左侧,后者始终保持在d的左侧。而b'c'也满足此属性,因为它们是使用满足该声明的子树bc进行递归调用的结果。

声明2(红色不变性)如果输入的rb1rb2符合红色不变性(如果一个节点是红色,则其两个子节点都是黑色),则app rb1 rb2中的所有节点也如此,除了可能的根节点。然而,只有当其参数之一具有红色根时,根才可能是“不红色的”。

证明 该证明通过对模式进行分支来完成。

  • For cases app E x = x and app x E = x the claim is trivial
  • For app (T R a x b) (T R c y d), the claim hypothesis tells us all of a, b, c, and d are black. It follows that app b c obeys the red invariant fully (it is not infrared).
    • If app b c matches T R b' z c' then its subtrees b' and c' must be black (and obey the red invariant), so T R (T R a x b') z (T R c' y d) obeys the red-invariant with an infrared root.
    • Otherwise, app b c produced a black node bc, so T R a x (T R bc y d) obeys the red invariant.
  • For app (T B a x b) (T B c y d) we only care that app b c will itself obey the red-invariant

    • If app b c is a red node, it can be infrared but its subtrees b' and c', on the other hand, must obey the red invariant completely. That means T R (T B a x b') z (T B c' y d) also obeys the red invariant.
    • Now, if bc turns out to be black, we call balleft a x (T B bc y d). The neat thing here is that we already know which two cases of balleft can be triggered: depending on whether a is red or black

      balleft (T R a x b) y c = T R (T B a x b) y c
      balleft bl x (T B a y b) = balance bl x (T R a y b)
      
      • In the first case, what ends up happening is that we swap the color of the left subtree from red to black (and doing so never breaks the red-invariant) and put everything in a red subtree. Then balleft a x (T B bc y d) actually looks like T R (T B .. ..) (T B bc y d), and that obeys the red invariant.

      • Otherwise, the call to balleft turns into balance a x (T R bc y d) and the whole point of balance is that it fixes root level red violations.

  • For app a (T R b x c) we know that a and b must be black, so app a b is not infrared, so T R (app a b) x c obeys the red invariant. The same holds for app a (T R b x c) but with letters a, b, and c permuted.

第三条声明(黑不变量),如果输入的rb1rb2遵守黑不变量(从根到叶子节点的任何路径上黑色节点数相同),并且具有相同的黑深度,则app rb1 rb2也遵守黑不变量并具有相同的黑深度。

证明 证明是通过模式分支进行的。

  • 对于 app E x = xapp x E = x 这两种情况,命题显然成立。
  • 对于 app (T R a x b) (T R c y d),我们知道由于 T R a x bT R c y d 的黑高度相同,它们的子树 abcd 也是如此。根据归纳假设,app b c 也将遵循黑不变量,并且具有相同的黑高度。现在,我们按照 case app b c of ... 分类讨论证明:
    • 如果 app b c 匹配 T R b' z c',那么它是红色的,并且其子树 b'c' 将与 app b c (本身)具有相同的黑高度,而后者又与 ad 具有相同的黑高度,因此 T R (T R a x b') z (T R c' y d) 遵守黑不变量并具有与其输入相同的黑高度。
    • 否则,app b c 产生一个黑节点 bc,但是这个节点与 ad 具有相同的黑高度,因此整个 T R a x (T R bc y d) 仍然遵循黑不变量并具有与其输入相同的黑高度。
  • 对于 app (T B a x b) (T B c y d),我们再次知道子树 abcd 都具有与 app b c 相同的黑高度。与以前一样,我们根据 case app b c of ... 进行分类讨论证明:
    • 如果 app b c 是形如 T R b' z c' 的红色节点,则我们再次得到 b'c'ad 具有相同的黑高度,因此 T R (T B a x b') z (T B c' y d) 也具有相同的黑高度。
    • 现在,如果 bc 是黑色的,我们按照之前的假设进行推理,将输出的 balleft a x (T B bc y d) 实际上表示为
      • T R (T B .. ..) (T B bc y d),其中 (T B .. ..) 只是将 a 重新着色为黑色,以便整个树满足黑不变量,并且其黑高度比任何一个 abcd 的黑高度都高一,也就是说与输入 T B a x bT B c y d) 具有相同的黑高度。
    • balance a x (T R bc y d)balance 保持黑不变量。
  • 对于 app a (T R b x c)app (T R a x b) c

    为什么它很快?

    我的Racket有点生疏,所以我没有一个很好的答案。一般来说,app 通过允许您在两个步骤中完成所有操作,使删除变得更快:您首先进入目标站点,然后继续向下合并子树,然后回到顶部修复不变量。

    在您提到的论文中,一旦您到达目标站点,就会调用 min/delete (我认为这是关键区别-旋转看起来非常相似),它将递归调用自身以查找可以放置在目标位置的子树中的元素,以及该元素被取出后子树的状态。我相信最后一部分是导致该实现性能不佳的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接