web-dev-qa-db-ja.com

AVLツリーでローテーションを実行する場所をどのようにして知っていますか?

だから私はAVLツリーを自習していて、その背後にある基本的な考え方を理解していますが、実際に実装するという直感が有効であることを確認したいだけです。

左回転で調べてみます

したがって、次の状況は簡単です。

      8
     / \
    7   10
   /
  6
 /
3

3を追加すると、ツリーは次のように再調整されます。

    8
   / \
  6   10
 / \
3   7

しかし、ローテーションは、3の追加に基づいていますか、それとも7をルートとするサブツリーの不均衡ですか?それは8を根とする木の不均衡に基づいていますか?

次の例は、私の考えでは、物事が少し毛むくじゃらになる場所です:

      9
     / \
    7   10
   / \
  6   8
 /
3

したがって、この場合、3が追加されたときに7のサブツリーは問題ないので、サブツリーを回転する必要はありません。ただし、9のツリーは3を追加すると不均衡になるため、回転を9に基づいています。

      7
     / \
    6   9
   /   / \
  3   8   10

それで、すぐに私のコードを書くときに、小さなサブツリーから大きなサブツリーまで、次のコードがうまくいくでしょうか?

疑似コード:

function balanceTree(Node n){

  if (n is not null){

    balanceTree(n.rightchild);
    balanceTree(n.leftchild);
  }

  if (abs(balanceFactor(n))>1){

    rotateAsNeeded(n);// rotate based on balance factor

  }

}

前もって感謝します!

30
Skorpius

あなたが投稿した疑似コードは正しくツリーのバランスをとります。とは言っても、実用的であるには非効率的です。ツリー全体を再バランス処理して、すべての挿入と削除にO(n)時間、バランスの取れたツリーを持つことで得られるすべての効率の向上を食い尽くす。

AVLツリーの背後にある考え方は、globallyツリーのリバランスは、localローテーション。つまり、挿入または削除を行い、ツリーのローテーションを行う必要がある場合、それらのローテーションはツリー内のランダムなスポットに表示されません。それらは常に、ノードの挿入または削除時に行ったアクセスパスに沿って表示されます。

たとえば、値3をこのツリーに挿入することに興味がありました。

      9
     / \
    7   10
   / \
  6   8

各ノードに関連付けられたバランス係数の違いを書き出すことから始めましょう(挿入と削除を効率的に行うことができるため、AVLツリーノードがこの情報を保存することが重要です)。

           9(+1)
         /       \
       7 (0)    10 (0)
      / \
  6(0)   8(0)

それでは、3を挿入するとどうなるか見てみましょう。これにより、3がここに配置されます。

           9(+1?)
          /       \
        7 (0?)    10 (0)
       /   \
   6(0?)   8(0)
   /
 3(0)

アクセスパス上のすべてのノードに?のマークを付けていることに注意してください。これは、それらのバランス係数が何であるかがわからなくなったためです。 6の新しい子を挿入したので、6ノードのバランス係数が+1に変更されます。

           9(+1?)
          /       \
        7 (0?)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

同様に、7の左側のサブツリーは高さが大きくなったので、そのバランス係数を増分する必要があります。

           9(+1?)
          /       \
        7 (+1)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

最後に、9の左側のサブツリーが1つ大きくなり、次のようになります。

           9(+2!)
          /       \
        7 (+1)    10 (0)
       /   \
   6(+1)   8(0)
   /
 3(0)

ここで、9のバランス係数は+2であることがわかります。つまり、ローテーションを実行する必要があります。コンサルティング WikipediaのすべてのAVLツリーローテーションの優れた表 、バランス係数が+2で、左側の子のバランス係数が+1であることがわかります。これは、次に示すように、右回転を行い、9の上に7を引くことを意味します。

        7(0)
       /   \
   6(+1)     9(0)
   /       /   \
 3(0)    8(0)   10 (0)

Etvoilà!ツリーのバランスが調整されました。

この修正手順を実行したときに、ツリー全体を調べる必要がなかったことに注意してください。代わりに、アクセスパスに沿って調べ、そこで各ノードを確認するだけで済みました。通常、AVLツリーを実装する場合、挿入プロシージャは次のことを行います。

  • ツリーがnullの場合:
    • バランス係数0のノードを挿入します。
    • 木の高さが1だけ増加したことを返します。
  • さもないと:
    • 挿入する値が現在のノードと一致する場合は、何もしません。
    • それ以外の場合は、ノードを適切なサブツリーに再帰的に挿入し、ツリーの高さの変化量を取得します。
    • サブツリーの高さが変化した量に基づいて、このノードのバランス係数を更新します。
    • これが一連のローテーションを義務付けている場合は、それらを実行します。
    • この木の高さの結果の変化を返します。

これらの操作はすべてローカルであるため、実行される総作業は、純粋にアクセスパスの長さに基づいています。この場合、AVLツリーは常にバランスがとれているため、O(log n)になります。

お役に立てれば!


PS:あなたの最初の例はこのツリーでした:

      8
     / \
    7   10
   /
  6
 /
3

ルートノードのバランス係数は+2であるため、このツリーは実際には有効なAVLツリーではないことに注意してください。 AVLアルゴリズムを使用してツリーのバランスを一貫して維持している場合、このケースに遭遇することはありません。

33
templatetypedef