赤黒木 (12)

朝練メモ。ちょっと間が空いたのですが続きを。一旦仕切り直しで再開。case 5. からです。最初の状態は以下で

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

ここから -3 な要素を削除する模様です。これ、回転の末に root なソレが代わるみたいです。たしかに root 要素を代えるくらい回転させないと駄目そうに見えます。
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 の子供は両方 nil です。ので z はそのまま。

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

x は nil で p は key が -2 の要素になります。

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

スルー。

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

(z の親の) p の左側要素は nil になります。

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

z は黒なので delete-fixup! 手続き突入。回転必須なのでここに入らないとアレですね。x は nil で p は z の親要素を指してます。

(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 も赤ではないのでスルー。

      (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))

ええと、x と p の左は nil なので b は #t ですね。そして p の右側要素は赤ではないので、w は key が -1.5 の要素を指します。

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

w の左右は nil で両方黒判定されますので then ブロックが評価されます。w が赤になって親のそのまた親を指す形で loop を呼び出してます。p が key が 0 の root 要素を、x が key が -2 の要素を指してる形。

    (if (or (nil? p) (red? x))
      (unless (nil? x)
        (paint-black! x))

条件式は両方偽ですね。

      (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))

x は p の左側要素なので b は #t になります。w は p の右側要素になるので key が 2 の node ですね。赤ではありません。

          (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 の右がまさに赤なのでそのまま、になるのかな。

              (set! (ref w 'color)
                    (if (nil? p) 'black (ref p 'color)))
              (paint-black! p)
              (paint-black! (right* w b))
              (left-rotate! tree p b)
              (paint-black! (root-of tree)))))))))
  • w の色は p が nil でなければ p と同じ色
  • p を黒に
  • w の右 (key が 3 の要素) を黒に

した上で left-rotate! が呼び出されます。p は root な要素で b は #t です。この時点の tree は以下なイメージ (のはず)。

  ;;      B:-2 => -2
  ;;        R:-1.5 => -1.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

順に見ていきます。

(define (left-rotate! tree x b)
  (let1 y (right* x b)

y には root の右要素 (key が 2) を指します。

    (set! (right* x b) (left* y b))
    (unless (nil? (left* y b))
      (set! (parent (left* y b)) x))
    (set! (parent y) (parent x))
  • x (root 要素で key が 0) の右を y (root 要素の右要素で key が 2) の左の要素 (key が 1) にしてます
  • 逆のリンクも張りかえてます
  • そして y の親を x の親にしています。
    (cond ((nil? (parent x))
           (set! (root-of tree) y))
          ((eq? x (left* (parent x) b))
           (set! (left* (parent x) b) y))
          (else
           (set! (right* (parent x) b) y)))

で、x は root 要素で親は nil なので y を root にしてますね。これで x と y が交代しました。

    (set! (left* y b) x)
    (set! (parent x) y)))

最後に

  • y の左が x
  • x の親は y

という形で x - y 間のリンクも張りなおして終了。以下な形になります。

  ;;        B:-2 => -2
  ;;          R:-1.5 => -1.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

なんか平衡になってる感。で、delete-node! に戻って z のリンクを全部切って終了なんでしょうか。

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

試験のコメントによればこれで当たりですね。

しかしこれ

全部のケイスを通過するよう試験書くのとか超面倒だな。