ツリー/グラフを走査するとき、幅優先と深さ優先の違いは何ですか?コーディングまたは擬似コードの例は素晴らしいでしょう。
これらの2つの用語は、ツリーを歩く2つの異なる方法を区別します。
違いを示すのがおそらく最も簡単でしょう。ツリーを考えてみましょう:
A
/ \
B C
/ / \
D E F
深さ最初の走査は、この順序でノードを訪問します
A, B, D, C, E, F
先に進む前にdown 1本の足で行くことに注意してください。
幅最初のトラバーサルは、この順序でノードを訪問します
A, B, C, D, E, F
ここでは、すべての方法で作業しますacross各レベルが下がる前に。
(トラバーサル順序にはあいまいさがあり、ツリーの各レベルで「読み取り」順序を維持するためにだまされていることに注意してください。どちらの場合でも、Cの前または後にBに到達できます。 Fの前または後のE。これは重要かどうかは関係なく、アプリケーションによって異なります...)
疑似コードを使用して、両方の種類のトラバーサルを実現できます。
Store the root node in Container
While (there are nodes in Container)
N = Get the "next" node from Container
Store all the children of N in Container
Do some work on N
2つの走査順序の違いは、Container
の選択にあります。
再帰的な実装は次のようになります
ProcessNode(Node)
Work on the payload Node
Foreach child of Node
ProcessNode(child)
/* Alternate time to work on the payload Node (see below) */
再帰は子を持たないノードに到達すると終了するため、有限の非循環グラフで終了することが保証されます。
この時点で、私はまだ少しだまされています。少し巧妙であれば、次の順序でノードをwork-onすることもできます。
D, B, E, F, C, A
これは深さ優先のバリエーションで、ツリーをさかのぼるまで各ノードで作業を行いません。ただし、子を見つけるために下の方に高いノードをvisitedしています。
このトラバーサルは、再帰的実装(最初の「作業」行の代わりに上記の「代替時間」行を使用)ではかなり自然であり、toohardではありません明示的なスタックを使用しますが、演習として残しておきます。
この図は、単語breadthおよびdepth 使用されています。
深さ優先探索アルゴリズムは、開始点から可能な限り迅速に遠ざけたいかのように動作します。
通常、Stack
を使用して、行き止まりに達したときにどこに行くべきかを記憶します。
従うべき規則:最初の頂点AをStack
にプッシュする
Javaコード:
public void searchDepthFirst() {
// Begin at vertex 0 (A)
vertexList[0].wasVisited = true;
displayVertex(0);
stack.Push(0);
while (!stack.isEmpty()) {
int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
// If no such vertex
if (adjacentVertex == -1) {
stack.pop();
} else {
vertexList[adjacentVertex].wasVisited = true;
// Do something
stack.Push(adjacentVertex);
}
}
// Stack is empty, so we're done, reset flags
for (int j = 0; j < nVerts; j++)
vertexList[j].wasVisited = false;
}
アプリケーション :深さ優先の検索は、ゲーム(および現実世界のゲームのような状況)のシミュレーションでよく使用されます。典型的なゲームでは、いくつかの可能なアクションの1つを選択できます。それぞれの選択肢はさらなる選択肢につながり、それぞれの選択肢はさらなる選択肢につながり、さらに拡大する可能性のツリー形のグラフへと続きます。
Queue
を使用して実装されます。Javaコード:
public void searchBreadthFirst() {
vertexList[0].wasVisited = true;
displayVertex(0);
queue.insert(0);
int v2;
while (!queue.isEmpty()) {
int v1 = queue.remove();
// Until it has no unvisited neighbors, get one
while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
vertexList[v2].wasVisited = true;
// Do something
queue.insert(v2);
}
}
// Queue is empty, so we're done, reset flags
for (int j = 0; j < nVerts; j++)
vertexList[j].wasVisited = false;
}
アプリケーション :幅優先検索では、最初に開始点から1エッジ離れたすべての頂点が検出され、次に2エッジ離れたすべての頂点が検索されます。これは、開始頂点から特定の頂点までの最短パスを見つけようとしている場合に便利です。
うまくいけば、Breadth-First検索とDepth-First検索を理解するのに十分なはずです。さらに読むには、Robert Laforeによる優れたデータ構造の本のグラフの章をお勧めします。
この二分木を考えると:
幅優先走査:
各レベルを左から右へトラバースします。
「私はG、私の子供はD、私は私の孫はB、E、H、K、彼らの孫はA、C、Fです」
- Level 1: G
- Level 2: D, I
- Level 3: B, E, H, K
- Level 4: A, C, F
Order Searched: G, D, I, B, E, H, K, A, C, F
深さ優先走査:
トラバーサルは、一度に全レベルで行われません。代わりに、トラバーサルは最初にツリーのDEPTH(ルートからリーフ)に飛び込みます。ただし、単純に上下するよりも少し複雑です
3つの方法があります。
1) PREORDER: ROOT, LEFT, RIGHT.
You need to think of this as a recursive process:
Grab the Root. (G)
Then Check the Left. (It's a tree)
Grab the Root of the Left. (D)
Then Check the Left of D. (It's a tree)
Grab the Root of the Left (B)
Then Check the Left of B. (A)
Check the Right of B. (C, and it's a leaf node. Finish B tree. Continue D tree)
Check the Right of D. (It's a tree)
Grab the Root. (E)
Check the Left of E. (Nothing)
Check the Right of E. (F, Finish D Tree. Move back to G Tree)
Check the Right of G. (It's a tree)
Grab the Root of I Tree. (I)
Check the Left. (H, it's a leaf.)
Check the Right. (K, it's a leaf. Finish G tree)
DONE: G, D, B, A, C, E, F, I, H, K
2) INORDER: LEFT, ROOT, RIGHT
Where the root is "in" or between the left and right child node.
Check the Left of the G Tree. (It's a D Tree)
Check the Left of the D Tree. (It's a B Tree)
Check the Left of the B Tree. (A)
Check the Root of the B Tree (B)
Check the Right of the B Tree (C, finished B Tree!)
Check the Right of the D Tree (It's a E Tree)
Check the Left of the E Tree. (Nothing)
Check the Right of the E Tree. (F, it's a leaf. Finish E Tree. Finish D Tree)...
Onwards until...
DONE: A, B, C, D, E, F, G, H, I, K
3) POSTORDER:
LEFT, RIGHT, ROOT
DONE: A, C, B, F, E, D, H, K, I, G
使用法(別名、なぜ気にするのか):
Depth First Traversalの方法と、それらの一般的な使用方法に関するこの簡単なQuoraの説明を本当に楽しみました。
"In-Order Traversalは値を印刷します[BST(バイナリ検索ツリー)の順序で]" "
「予約検索は、[バイナリ検索ツリー]のコピーを作成するために使用されます。」
"後順走査は[バイナリ検索ツリー]の削除に使用されます。"
https://www.quora.com/What-is-the-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing
いくつかのコード行を切り替えるだけで、いずれかのアルゴリズムが得られるように両方を書くのは面白いと思うので、最初のようにディレマはそれほど強くないことがわかります。
私は個人的に、BFSが景観をflood濫させると解釈するのが好きです。低標高の地域が最初にflood濫し、その後に高標高の地域がflood濫します。地理学の本で見られるように、地形の標高を等値線と考えると、物理学と同じように、BFSが同じ等値線の下のすべての領域を同時に満たすのは簡単にわかります。したがって、高度を距離またはスケーリングされたコストとして解釈すると、アルゴリズムの非常に直感的なアイデアが得られます。
これを念頭に置いて、幅優先探索の概念を簡単に適応させて、最小スパニングツリー、最短パス、および他の多くの最小化アルゴリズムを簡単に見つけることができます。
DFSの直観的な解釈はまだ見ていません(迷路に関する標準的なものだけですが、BFSのものと洪水ほど強力ではありません)ので、私にとってはBFSは上記の物理現象とよりよく相関しているようですDFSは、合理的なシステム(つまり、チェスゲームで進むか迷路から出るかを決定する人またはコンピューター)の選択ディレマとの相関が高くなります。
したがって、私にとっての違いは、自然現象が実際の伝搬モデル(横断)と最もよく一致することにあります。