はじめての C

二分木 続き 8

int avl_insert(struct Avl **tree, struct Avl *new_item)
{
/* empty tree */
if (*tree == NULL) {
*tree = new_item;
return 1;
}
if (compare((*tree)->data, new_item->data) > 0) {
/* Move left */
if ((*tree)->left != NULL) {
if (avl_insert(&(*tree)->left, new_item) != 0) {
/* Balancing act */
if ((*tree)->balance++ < 1)
return (*tree)->balance;
else if ((*tree)->left->balance > 0) {
/* Case 1 */
rotate_right(tree);
(*tree)->balance = 0;
(*tree)->right->balance = 0;
}
else {
/* Case 2 */
rotate_left(&(*tree)->left);
rotate_right(tree);
/* Adjust balance factors */
if ((*tree)->balance == 1) {
(*tree)->left->balance = -1;
(*tree)->right->balance = 0;
}
else if ((*tree)->balance == 0) {
(*tree)->left->balance = 0;
(*tree)->right->balance = 0;
}
else {
(*tree)->left->balance = 0;
(*tree)->right->balance = 1;
}
(*tree)->balance = 0;
}
}
else
return 0;
}
else {
(*tree)->left = new_item;
return ++(*tree)->balance;
}
}
else if (compare((*tree)->data, new_item->data) < 0) {
/* Move right */
if ((*tree)->right != NULL) {
if (avl_insert(&(*tree)->right, new_item) != 0) {
/* Balancing act */
if ((*tree)->balance++ < 1)
return (*tree)->balance;
else if ((*tree)->right->balance > 0) {
/* Case 1 */
rotate_left(tree);
(*tree)->balance = 0;
(*tree)->left->balance = 0;
}
else {
/* Case 2 */
rotate_right(&(*tree)->right);
rotate_left(tree);
/* Adjust balance factors */
if ((*tree)->balance == 1) {
(*tree)->left->balance = -1;
(*tree)->right->balance = 0;
}
else if ((*tree)->balance == 0) {
(*tree)->left->balance = 0;
(*tree)->right->balance = 0;
}
else {
(*tree)->left->balance = 0;
(*tree)->right->balance = 1;
}
(*tree)->balance = 0;
}
}
else
return 0;
}
else {
(*tree)->right = new_item;
return ++(*tree)->balance;
}
}
return 0;
}

これは ほぼ 直訳なので、もし 疑似コードが 理解できて、C の 基本のところを 精通していれば、関数を 読み解くのに 何の 問題も ないでしょう。

とても 驚くことに (私は、それが 実現したときに 本当に 驚いたのですが)、この関数では line 毎の description を 必要としません。

すでに 回転でも 検討しましたが、再帰に 通じてる人なら だれでも、この状況で 何の 問題も 起こりません。 そして コードの残りは、単に flag の 反転 - flipping - と 比較とに なります。

もし まだ 困難を かかえてるのなら、紙と 鉛筆とを 引き寄せて、壊れて ドロドロになった - crushed-pulp - とでも 呼びたい AVL木に なんらかの 値を 挿入してください。

いいかえると、書き出してみることで、そのアイデアを 掴むのです。

ちょっとした tutorial ... ですか ?

もっと 複雑な アルゴリズムの コードを 1つ 与えて、それを 自分で 計算するよう 命じることです :p

この 上級の tutorial の やり方だと、早急に 基礎を つくりあげられるので、より多くの 要点を つけ加えられることと、その後の (説明形式での) 失敗を 少なくすることが できます。

さて、考えるのですが、つりあいの とれた 木について、本や tutorial では、insertion (関数) に 対しては、動かすための (ときには 不十分な あるいは 全ケースでの) 全ての ソースコードを 含む、たっぷりの 時間と スペースとが 与えられていますね。 でも、deletion (関数) に 関しては、まったく 無視されています。

もっと 悪いと、課題だけで 止めてしまってます。

私の 判断ですが、(それらの) 筆者は、簡単な ところは 説明も せず、難しい 部分は 最小限の 説明のみで 済ませて、課題だけで 止めてしまってるのです。

それで、avl_delete (関数) についても、その 疑似コードと C の ソースとを 示すことにしましょう。

というのも、その方が、ちょっとだけでも、思いやりが あるでしょうしね :p ▽


# Returns 1 if height changes, 0 otherwise
sub avl_remove(tree, item)
  heir
  rv

  if tree == item
    # We found it, now delete it
    if tree.left == null AND tree.right == null
      # No chirdren, unlink the node
      tree = null
      return 1
    elif tree.left == null
      # Replace node with right child
      tree = tree.right
      return 1
    elif
      # Replace node with left child
      tree = tree.left
      return 1
  endif
  if tree_balance < 0
    # Find left successor
    heir = tree.left
    while heir.right != null
      heir = heir.right
  else
    # Find right successor
    heir = tree.right
    while heir.left != null
      heir = heir.left
  endif
  # Rebalance
  rv = avl_remove(tree, heir)
  # Remove node
  heir.left = tree.left
  heir.right = tree.right
  heir.balance = tree.balance
  tree = heir
  if heir.balance == 0
    return rv;
  endif
  # Search
  if tree > item
    # Move left
    if tree.left != null
      if avl_remove(tree.left, item) != 0
        if tree.balance < 1
          # Still balanced
          return tree.balance += 1
        elif tree.right.balance == 0
          rotate_left(tree)
          tree.balance = -1
          tree.left.balance = 1
        elif tree.right.balance == 1
          rotate_left(tree)
          tree.balance = 0
          tree.left.balance = 0
        else
          rotate_right(tree.right)
          rotate_left(tree)
          # Adjust balance factors
          if tree.balance == 1
            tree.left.balance = -1
            tree.right.balance = 0
          elif tree.balance == 0
            tree.left.balance = 0
            tree.right.balance = 0
          else
            tree.left.balance = 0
            tree.right.balance = 1
        endif
        tree.balance = 0
      endif
      return 1
    endif
  endif
  elif tree < item
    # Move right
    if tree.right != null
      if avl_remove(tree.right, item) != 0
        if tree.balance > -1
          # Still balanced
          return tree.balance -= 1
        elif tree.left.balance == 0
          rotate_right(tree)
          tree.balance = 1
          tree.right.balance = -1
        elif tree.left.balance == -1
          rotate_right(tree)
          tree.balance = 0
          tree.right.balance = 0
        else
          rotate_left(tree.left)
          rotate_right(tree)
          # Adjust balance factors
          if tree.balance == 1
            tree.left.balance = -1
            tree.right.balance = 0
          elif tree.balance == 0
            tree.left.balance = 0
            tree.right.balance = 0
          else
            tree.left.balance = 0
            tree.right.balance = 1
          endif
          tree.balance = 0
        endif
        return 1
      endif
    endif
  endif

  return 0
end sub

検索 (の箇所は) avl_insert (関数) で 見たところと 基本としては 同じことです。

都合の よいことに (検索のような) 考え方は 一度 理解できれば、多くの場面で 再利用が できます。

それで、たとえ コードが 長くて 複雑に 見えたとしても、その大きな部分は 理解できてきます。

もう一度 励ましますけど、どのように すべてが 動くのかが 理解できるまでは、このロジックを 紙の上に 書き出すように してください。

そのことが (例えば) ルーティンの 内側での 動きの異なった 10 の 方法を 説明しようと 試みることなんかより、ずっと より 有益でしょう。 それに、私を タイピングでの 大文字のトンネルから 救い出してくれるでしょうしね :p



書いたものを いじってみるのは 終わりましたか ?

OK、これが C に 翻訳したものです ▽