web-dev-qa-db-ja.com

バイナリ検索ツリーでの検索がO(log(n))になるのはなぜですか?

[〜#〜] bst [〜#〜]で値を検索するときに、ノードを探している値と比較するたびにツリーの半分を残す方法がわかります。

しかし、時間の複雑さがO(log(n))である理由を理解できません。だから、私の質問は:

N要素のツリーがある場合、ツリーを検索して特定の値が存在するかどうかを確認する時間の複雑さがO(log(n))である理由は、どうやって取得するのでしょうか。

21
Hommer Smith

あなたの質問はよく答えられているようです ここ しかし、あなたの特定の質問に関連して要約するには、それを逆に考える方が良いかもしれません。 「ノード数が増えると、BSTソリューション時間はどうなりますか?」

基本的に、BSTでは、ノードの数を2倍にするたびに、ソリューションまでのステップ数を1つ増やすだけです。これを拡張するには、4回のノードで2つの追加のステップを実行します。ノードの8倍は、3つの追加ステップを提供します。ノードの16倍は4つの追加ステップを与えます。等々。

これらのペアの最初の数の2を底とする対数は、これらのペアの2番目の数です。これはバイナリ検索であるため、2を底とするログです(問題のスペースをステップごとに半分にします)。

32

私にとって最も簡単な方法は、log2(n)のグラフを確認することでした。ここで、nはバイナリツリーのノード数です。テーブルとしては、次のようになります。

_          log2(n) = d

          log2(1) = 0
          log2(2) = 1 
          log2(4) = 2
          log2(8) = 3
          log2(16)= 4 
          log2(32)= 5
          log2(64)= 6             
_

次に、小さなバイナリツリーを描画します。これは、深さd = 0からd = 3になります。

_            d=0          O
                        / \
            d=1        R   B
                      /\   /\
            d=2      R  B R  B
                    /\ /\ /\ /\
            d=3    R B RB RB R B
_

したがって、ツリー内のノードの数nとして、実質的にdoubles(たとえば、nは7から15に進むにつれて8増加します(これはほとんどa倍加)深さdがd = 2からd = 3になると、1ずつ増加します。したがって、必要な処理(または所要時間)の追加量は1追加の計算(または反復)だけ増加します。 、処理量はdに関連しているため。

ノード数を2倍にした後、すべてのノードnから必要なノードを見つけるために、d = 2からd = 3まで、深さdの1レベルだけ下に行くことがわかります。これは、ツリー全体を検索したためです。必要なノードを見つけるために検索する必要があるツリーの半分を検索しました。

これはd = log2(n)と書くことができます。ここで、dは、ツリーにn個のノードがある場合に、ツリーの任意のノードに到達するために(平均して)実行する必要がある計算量(反復回数)を示します。

7
Will

これは数学的に非常に簡単に表示できます。

それを紹介する前に、はっきりさせておきます。 バランスのとれた二分探索木での検索または検索の複雑さはO(log(n))です。一般に、二分探索木ではO(n)です。以下に両方を示します。

バランスのとれた二分探索木では、最悪の場合、私が探している値は木の葉にあります。 BSTの順序付けられた構造により、ツリーの各レイヤーを1回だけ確認することで、ルートからリーフまで基本的にトラバースします。したがって、必要な検索の数は、ツリーのレイヤーの数です。したがって、問題は結局、n個のノードを持つツリーの層の数の閉じた形式の式を見つけることになります。

ここで、簡単な帰納法を実行します。レイヤーが1つだけのツリーには、ノードが1つしかありません。 2層のツリーには1 + 2ノードがあります。 3層1 + 2 + 4ノードなど。パターンは明確です。k個の層を持つツリーは、正確に

n = 2 ^ 0 + 2 ^ 1 + ... + 2 ^ {k-1}

ノード。これは幾何学的なシリーズであり、

n = 2 ^ k-1

同様に:

k = log(n + 1)

Big-ohはnの大きな値に関心があるため、定数は無関係です。したがって、O(log(n))複雑さです。

同じ結果を表示する別の方法を紹介します。値を探している間、ツリーを常に2つに分割し、これをk回行う必要があるため、kはレイヤー数であり、次のようになります。

(n + 1)/ 2 ^ k = 1

これはまったく同じ結果を意味します。 n + 1の+1がどこから来ているかについて納得する必要がありますが、nの大きな値について話しているので、それに気を付けなくても大丈夫です。

次に、一般的なバイナリ検索ツリーについて説明します。最悪の場合、それは完全に不均衡です。つまり、そのノードのすべてに子が1つだけあります(そしてリンクされたリストになります)。 https://www.cs.auckland.ac.nz/~jmor159/PLDS210/niemann/s_fig33.gif

この場合、葉の値を見つけるには、すべてのノードを反復処理する必要があるため、O(n)になります。

最後の注意点は、これらの複雑さは、検索だけでなく、挿入および削除操作にも当てはまることです。

(10 repポイントに達したら、見栄えの良いLatex数学スタイリングを使って方程式を編集します。SOは今は許可しません。)

4
FatihAkici

O(log n)係数が含まれているランタイムが表示されるときはいつでも、 「何らかのオブジェクトのサイズを定数で除算し続ける」という形式の何かを見ている可能性が非常に高くなります。 。 " したがって、おそらくこの質問について考える最良の方法は、二分探索ツリーでルックアップを行っているとき、定数係数によって正確に何が削減されているのか、そしてその定数は何なのかということです。 ?

まず、次のような完全にバランスのとれたバイナリツリーがあるとします。

                     *
              /             \
             *               *
          /     \         /     \
         *       *       *       *
        / \     / \     / \     / \
       *   *   *   *   *   *   *   *

検索を実行する各時点で、現在のノードを確認します。それがあなたが探しているものなら、素晴らしい!あなたは完全に終わりました。一方、そうでない場合は、左側のサブツリーまたは右側のサブツリーに下降してから、このプロセスを繰り返します。

2つのサブツリーの1つに入ると、本質的には「他のサブツリーの内容はまったく気にしません」と言っています。その中のすべてのノードを捨てます。そしてそこにはいくつのノードがありますか?まあ、簡単な目視検査-理想的にはいくつかの素敵な数学でフォローアップ-で、ツリー内のノードの約半分を投げていることがわかります。

つまり、ルックアップの各ステップで、(1)探しているノードを見つけるか、(2)ツリー内のノードの半分を破棄します。各ステップで一定量の作業を行っているので、O(log n)動作の特徴的な動作を見ています。作業は各ステップで一定の係数で低下するため、対数的にしか実行できません。回。

もちろん、すべての木がこのように見えるわけではありません。 AVLツリーには楽しいプロパティがあり、サブツリーに降りるたびに、ノード全体のほぼ黄金比を捨てます。したがって、これにより、ノードが不足する前に対数的に多くのステップしか実行できないことが保証されます。つまり、O(log n)の高さになります。赤/黒のツリーでは、各ステップでノードの合計の(おおよそ)4分の1が破棄されます。一定の係数で縮小しているため、希望するO(log n)ルックアップ時間を再び取得できます。非常に楽しいスケープゴートツリーには、どれだけ緊密にバランスが取れているかを決定するために使用される調整可能なパラメーターがありますが、繰り返しますが、実行するすべてのステップがこの調整可能なパラメーターに基づいて一定の要因を捨て、O(log n)ルックアップを提供します。

ただし、この分析は不均衡なツリーに対しては失敗します。純粋に縮退したツリー(すべてのノードにちょうど1つの子があるツリー)がある場合、ツリーを下に移動するたびに、一定の割合ではなく、単一のノードだけを投げます。つまり、ルックアップ時間は最悪の場合O(n)になります。これは、subtractnからの定数はO(n)です。

1
templatetypedef

N要素のツリーがある場合、ツリーを調べて特定の値が存在するかどうかを確認する時間の複雑さがO(log(n))である理由は、どうやって取得するのでしょうか。

それは真実ではありません。デフォルトでは、バイナリ検索ツリーでのルックアップはO(log(n))ではなく、nはノードの数です。最悪の場合、O(n)になります。たとえば、次のシーケンス_n, n - 1, ..., 1_の値を(同じ順序で)挿入すると、ツリーは次のように表されます。

_                  n
                 /
              n - 1
               /
            n - 2
             /
           ...
           1
_

値_1_のノードのルックアップは、O(n)時間複雑です。

ルックアップをより効率的にするには、ツリーはbalancedでなければならず、その最大の高さはlog(n)に比例します。このような場合、検索の時間の複雑さはO(log(n))です。これは、リーフの検索がlog(n)演算によって制限されるためです。

ただし、すべてのBinary Search TreeBalanced Binary Search Treeであるとは限りません。 O(log(n))時間の複雑さを保証するには、それをバランスさせる必要があります。

0
Anatolii