私が受講しているアルゴリズムコースでは、深さ優先探索(DFS)は幅優先探索(BFS)よりもはるかにスペース効率が高いと言われています。
何故ですか?
それらは基本的に同じことを行っていますが、DFSでは現在のノードの後継をスタックし、BFSでは後継をキューに入れています。
あなたの混乱は、DFSアルゴリズムがFIFOキューをLIFOスタックに置き換えることによってBFSアルゴリズムから取得できると思われるという事実から生じています。
これはよくある誤解です-それは単に真実ではありません。従来のDFSアルゴリズムは、BFSキューをスタックに置き換えることによって取得することはできません。これらのアルゴリズムの違いははるかに重要です。
BFSアルゴリズムを使用して、FIFOキューをLIFOスタックに置き換えるだけで、疑似-)と呼ばれるものが得られます。 DFSアルゴリズム。この疑似DFSアルゴリズムは、DFS頂点順方向トラバーサルシーケンスを実際に正しく再現しますが、DFSスペース効率がなく、DFS逆方向トラバーサル(バックトラッキング)をサポートしません。
一方、真の古典的なDFSは、このような単純なキューからスタックへの置き換えではBFSから取得できません。従来のDFSは、コア構造が大幅に異なる完全に異なるアルゴリズムです。真のDFSは、真に再帰的アルゴリズムであり、バックトラッキングの目的でスタックを使用します。頂点検出の「フロント」を格納するためではありません(BFSの場合のように)。その最も直接的な結果は、DFSアルゴリズムでは、最大スタック深度がDFSトラバーサルの原点頂点からの最大距離に等しいことです。 BFSアルゴリズム(前述の疑似DFSと同様)では、最大キューサイズは、最大の頂点検出フロントの幅に等しくなります。
DFSとBFS(および疑似DFS)のピークメモリ消費量の違いを示す最も顕著で極端な例は、スターグラフです。単一の中央の頂点が多数で囲まれています(たとえば、1000
)。各周辺頂点がエッジによって中央頂点に接続されている周辺頂点の数。中央の頂点を原点としてこのグラフでBFSを実行すると、キューのサイズはすぐに1000
にジャンプします。疑似DFSを使用する場合(つまり、キューをスタックに置き換えるだけの場合)、同じことが明らかに起こります。ただし、従来のDFSアルゴリズムでは、このグラフ全体をトラバースするために必要なスタックの深さは1
(!)のみです。違いを見ます? 1000
対1
。これが、DFSのスペース効率の向上が意味することです。
基本的に、アルゴリズムに関する本を読んで、古典的なDFSの説明を見つけ、それがどのように機能するかを確認してください。 BFSとDFSの違いは、単なるキューとスタックよりもはるかに広範囲であることに気付くでしょう。
P.S.また、BFSでのピークメモリ消費量が少ないグラフの例を作成できることも言う必要があります。したがって、DFSのスペース効率の向上に関する記述は、「平均して」いくつかの暗黙のクラスの「ニース」グラフに適用される可能性があるものと見なす必要があります。
DFSでは、完全にバランスの取れたツリーで深さO(log(n))
に線形するためのスペースのみが必要ですが、BFS(幅優先探索)にはO(n)
が必要です(ツリーの最も広い部分が最も低い深さです)これは、バイナリツリーにn/2ノードあります)。
例:
1
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
2 2
/ \ / \
/ \ / \
/ \ / \
/ \ / \
3 3 3 3
/ \ / \ / \ / \
/ \ / \ / \ / \
4 4 4 4 4 4 4 4
/ \ / \ / \ / \ / \ / \ / \ / \
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
DFSにはスペースが必要です:4
BFSは最後から2番目の行スペースに必要です8
そして、分岐係数が高いと悪化します
DFSでは、使用されるスペースはO(h)です。ここで、hはツリーの高さです。
BFSでは、使用されるスペースはO(w)です。ここで、wはツリーの「幅」です。
典型的な二分木(つまり、ランダムな二分木)では、w = Omega(n)およびh = O(sqrt(n))です。
バランスの取れたツリーでは、w = Omega(n)およびh = O(log n)です。