赤黒木 (13)

朝の続きに着手。case 7a です。
最初の状態は以下な模様。

  ;;          B:-2 => -2
  ;;        R:-1.5 => -1.5
  ;;            R:-1 => -1
  ;;          B:-0.5 => -0.5
  ;;      B:0 => 0
  ;;          R:0.5 => 0.5
  ;;        B:1 => 1
  ;;    B:2 => 2
  ;;        B:2.5 => 2.5
  ;;      B:3 => 3
  ;;        B:3.5 => 3.5
  ;;          R:4 => 4

直前の状態からここまでもってくる様子も確認してみたいですが、なんとなく赤赤になる (-0.5 を追加する時) ので回転して -1 が赤で追加、という形なのかどうか。
で、上記の状態から黒の -2 な node を削除するみたい。

  ;;   case 7a  deleting black node, which is a left child of its parent,
  ;;            and its sibling is black, and sibling's left child is red.
  ;;            This goes through a nested if-statement in delete_node1()
  ;;            and involves swapping sibling.

削除後のイメージなコメントが無いですね。とりあえずおいかけてみます。つうかマンガ描いてみたらかなりバランス悪い木ですね。赤黒てきには平衡ですが。
まず以下。

  (let1 z (if (and (not (nil? (left z))) (not (nil? (right z))))
            (let1 y (successor z)
              (set! (ref z 'key) (ref y 'key))
              (set! (ref z 'value) (ref y 'value))
              y)
            z)

z は末端 node ですので z はそのまま。

    (let ((x (left* z (not (nil? (left z)))))
          (p (parent z)))

x は nil (x の右) で p は key が -1.5 の node になります。

      (unless (nil? x)
        (set! (parent x) p))

スルー。

      (if (nil? p)
        (set! (root-of tree) x)
        (set! (left* p (eq? z (left p))) x))

p は nil ではないので z な node が切り離し。p の左要素は nil になります。

      (when (black? z)
        (delete-fixup! tree x p))

z は黒なので delete-fixup! 手続きを呼び出します。x は nil で p は z の親の key が -1.5 の node になります。

(define (delete-fixup! tree x p)
  (let loop ((x x) (p p))
    (if (or (nil? p) (red? x))
      (unless (nil? x)
        (paint-black! x))

p は nil ではなく、x は nil ですので、ここはスルー。

      (let1 b (eq? x (left p))
        (let1 w (let1 w (right* p b)
                  (if (red? w)
                    (begin (paint-black! w)
                           (paint-red! p)
                           (left-rotate! tree p b)
                           (right* p b))
                    w))

p の左は nil なので b は #t ですね。で、w は p の右の key が -0.5 な要素を指します。ここは黒なので w はそのまんま。

          (if (and (black? (left w)) (black? (right w)))
            (begin (paint-red! w)
                   (loop p (parent p)))

w の子供は片方赤なのでここもスルーですね。

            (let1 w (if (black? (right* w b))
                      (begin (unless (nil? (left* w b))
                               (paint-black! (left* w b)))
                             (paint-red! w)
                             (right-rotate! tree w b)
                             (right* p b))
                      w)

んーと、w の右は nil なので黒。ここ、初めて通過するパスかも。

  • w の左 (key が -1) は nil ではないので黒になります (赤でした)
  • w は赤になります

で、right-rotate! が呼び出されるのか。呼び出し直前の部分木が以下。

R:-1.5
    B:-1
  R:-0.5

w は key が -1 な要素を指してます。よく見ると赤連続ですね。で、right-rotate! はトレイズ略で以下になります。

R:-1.5
  B:-1
    R:-0.5

で、元に戻りますね。ええとこうなった結果、w は key が -1 な node を指す形になるのかな。で、以下。

              (set! (ref w 'color)
                    (if (nil? p) 'black (ref p 'color)))
              (paint-black! p)
              (paint-black! (right* w b))

あら、w の色が再度赤になり、親の色が黒になるのか。そして p の孫も黒になり、で以下?

B:-1.5
  R:-1
    B:-0.5

で、

              (left-rotate! tree p b)

が呼び出されます。ここも詳細スルーで以下になるのかな。

      B:-1.5
    R:-1
      B:-0.5
  B:0

ありゃ、これで終わりそうだな。以下な状態になるのかどうか。

  ;;          B:-1.5 => -1.5
  ;;        R:-1 => -1
  ;;          B:-0.5 => -0.5
  ;;      B:0 => 0
  ;;          R:0.5 => 0.5
  ;;        B:1 => 1
  ;;    B:2 => 2
  ;;        B:2.5 => 2.5
  ;;      B:3 => 3
  ;;        B:3.5 => 3.5
  ;;          R:4 => 4

一応平衡に見えますね。大丈夫かな。

これ

ddd みたくリンクと要素が見えるカンジな UI があってステップ実行できたら面白いのに、と思ってしまいました。

ちなみに

上記から 0.5 な node を削除するのが次の試験らしいんですが、赤なのでもう少しおいかけてみます。
# 実は止めようと思ってた (を
delete-node! 手続き先頭から確認。

  (let1 z (if (and (not (nil? (left z))) (not (nil? (right z))))
            (let1 y (successor z)
              (set! (ref z 'key) (ref y 'key))
              (set! (ref z 'value) (ref y 'value))
              y)
            z)

ええと、末端要素なので z はそのまんま。

    (let ((x (left* z (not (nil? (left z)))))
          (p (parent z)))

z は末端要素なので x は nil、p は key が 1 の node になります。

      (unless (nil? x)
        (set! (parent x) p))

スルー。

      (if (nil? p)
        (set! (root-of tree) x)
        (set! (left* p (eq? z (left p))) x))

p から z へのリンクが切れます。

      (when (black? z)
        (delete-fixup! tree x p))

z は赤なのでスルー。

      (set! (left z) #f)
      (set! (right z) #f)
      (set! (parent z) #f))))

後は z のリンクを切る処理。ということで以下になるのでしょうか。イメージ無いので超不安。

  ;;          B:-1.5 => -1.5
  ;;        R:-1 => -1
  ;;          B:-0.5 => -0.5
  ;;      B:0 => 0
  ;;        B:1 => 1
  ;;    B:2 => 2
  ;;        B:2.5 => 2.5
  ;;      B:3 => 3
  ;;        B:3.5 => 3.5
  ;;          R:4 => 4

平衡になっている、はず。。
で、次に 2 と -1 が削除される模様。残りが 4 つある模様。色々とへろへろなので今日は早めにクタバります。