幅優先と深さ優先


171

ツリー/グラフをトラバースするとき、幅優先と深さ優先の違いは何ですか?コーディングや疑似コードの例はすばらしいでしょう。


6
ウィキペディアをチェックしましたか(深さ優先幅優先)。これらのページには、たくさんのきれいな写真とともにコード例があります。
rmeador 2009年

私もその考えを持っていましたが、与えられた例はwikipediaで見つかったものよりも少しいいです...
jonnybazookatone

回答:


291

これらの2つの用語は、木を歩く2つの異なる方法を区別します。

違いを示すだけでおそらく最も簡単です。ツリーについて考えてみましょう。

    A
   / \
  B   C
 /   / \
D   E   F

深さ優先走査の順にノードを訪問します

A, B, D, C, E, F

次のステップに進む前に、片方の脚をずっと下がっていることに注意してください。

最初のトラバーサルがこの順にノードを訪問します

A, B, C, D, E, F

ここでは、下に行く前に、各レベル全体で作業します。

(トラバーサル順序にはあいまいさがいくつかあることに注意してください。ツリーの各レベルで「読み取り」順序を維持するためにだましました。どちらの場合でも、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) */

再帰は、子のないノードに到達したときに終了するため、有限の非循環グラフで終了することが保証されています。


この時点で、私はまだ少しごまかしています。少し利口であなたもできる仕事上のノードを順に:

D, B, E, F, C, A

これは深さ優先のバリエーションであり、ツリーを上に戻るまで各ノードで作業を行いません。ただし、途中で上位のノードを訪問して、子供を見つけました。

このトラバーサルは再帰的な実装ではかなり自然であり(最初の "Work"行の代わりに上記の "Alternate time"行を使用)、明示的なスタックを使用する場合はそれほど難しくありませが、練習として残しておきます。


@dmckeeありがとう!「ノードでペイロードを処理する」という意味ですか?
バットブラット

4
深さ優先バージョンを変更して、接頭辞(A, B, D, C, E, F-最初に表示されたもの)、接頭辞(- D, B, A, E, C, Fソートに使用:AVLツリーとして追加してから、接尾辞を読み取る)、または後置(D, B, E, F, C, A表示された代替)トラバーサルを取得できることに注意してください。名前は、ルートを処理する位置によって与えられます。infixはバイナリツリーに対してのみ意味があることに注意してください。@batbratそれらは名前です...あなたが尋ねてからの時間を考えると、おそらくすでに知っています。
Theraot、2015年

@Theraotそれを追加してくれてありがとう!はい、私はこれらの種類のトラバーサルと、なぜInfixが二分木に対してのみ意味があるのか​​を知っています。
batbrat

空間または時間の複雑性が高いソリューションを判断するにはどうすればよいですか?
IgorGanapolsky 2016年

1
@IgorGanapolskyは原則として両方で同じである必要があります(結局のところ、それらは基本的に同じコードを使用します)。より興味深い質問は、それらがキャッシュとワーキングセットにどのように影響するかですが、それはツリーの形態に依存すると思います。
dmckee ---元モデレーターの子猫2016

95

用語を理解する:

この写真は、深さという言葉が使用れているコンテキストについての考えを与えるはずです。

幅と深さを理解する


深さ優先検索:

深さ優先検索

  • 深さ優先の検索アルゴリズムは、開始点からできるだけ早く離れたい場合と同じように機能します。

  • それは一般的にStack行き止まりに到達したときにどこに行くべきかを覚えておくためにを使用します。

  • 従うべきルール:最初の頂点Aを Stack

    1. 可能であれば、隣接する未訪問の頂点にアクセスし、訪問済みとしてマークして、スタックにプッシュします。
    2. ルール1に従うことができない場合は、可能であれば、スタックから頂点をポップします。
    3. ルール1またはルール2に従えない場合は、これで完了です。
  • 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ます。
  • 従うべき規則:開始頂点Aを現在の頂点にする
    1. 現在の頂点に隣接する次の未訪問の頂点(存在する場合)にアクセスし、マークを付けて、キューに挿入します。
    2. 未訪問の頂点がないためにルール1を実行できない場合は、キューから頂点を削除し(可能な場合)、それを現在の頂点にします。
    3. キューが空のためにルール2を実行できない場合は、完了です。
  • 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エッジ離れているすべての頂点が検出されます。これは、開始頂点から特定の頂点までの最短パスを見つけようとしている場合に役立ちます。

うまくいけば、幅優先検索と深さ優先検索を理解するのに十分なはずです。詳細については、Robert Laforeによる優れたデータ構造の本のグラフの章をお勧めします。


6
私がさらに10票の投票権を持っていたとしたら、そうします。
snrは

あなたは報奨金を授与でき@snr;)
雪の

@スノーはそのディレクティブを教えてくれればできます。方法がわかりません。
snr 2019年

@snrに感謝します。最初の賞金を受け取ってとても嬉しく思います。とても感謝しています。
Yogesh Umesh Vaity

1
@Snowに感謝します。私の回答が役に立ってくれてありがとう。
Yogesh Umesh Vaity

4

この二分木を考えると:

ここに画像の説明を入力してください

幅優先トラバーサル:
各レベルを左から右にトラバースします。

「私は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

深さ優先トラバーサル:
レベル全体で一度にトラバーサルを行うことはできません。代わりに、トラバーサルは最初にツリーの(ルートからリーフへの)デプスに飛び込みます。ただし、単純に上下するよりも少し複雑です。

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

使用法(別名、なぜ気にするのか):
深さ優先トラバーサルメソッドの簡単なQuoraの説明と、それらが一般的にどのように使用されるかを本当に楽しんだ
。 「
「プレオーダートラバーサルは、[バイナリ検索ツリー]のコピーを作成するために使用されます。」
「ポストオーダートラバーサルは、[バイナリ検索ツリー]を削除するために使用されます。」
https://www.quora.com/What-is-the-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing


2

両方のコードをいくつかのコード行を切り替えるだけでどちらかのアルゴリズムが得られるように両方を書くのは興味深いと思います。そうすれば、最初のように思えるほどディレマが強くないことがわかります。 。

私は個人的に、BFSを景観の洪水として解釈するのが好きです。低高度の地域が最初に洪水になり、その後にのみ高高度の地域が続きます。地理の本に見られるように、地形の標高を等高線と考えると、物理学の場合と同じように、BFSが同じ等高線の下のすべての領域を同時に満たすことが簡単にわかります。したがって、高度を距離またはスケーリングされたコストとして解釈すると、アルゴリズムをかなり直感的に理解できます。

これを念頭に置いて、幅優先検索の背後にある考え方を簡単に適用して、最小全域木を簡単に、最短経路で、そして他の多くの最小化アルゴリズムを見つけることができます。

私はまだDFSの直感的な解釈は見ていません(迷路に関する標準的な解釈のみですが、BFSやフラッディングほど強力ではありません)ので、私にとっては、BFSは上記の物理現象とよりよく相関しているようです。 DFSは、合理的なシステム(つまり、チェスゲームや迷路から抜け出すためにどちらの動きをするかを決定する人やコンピューター)の選択肢のディレマとよりよく相関しています。

したがって、私にとって両者の違いは、どの自然現象が実際の伝搬モデル(トランスバーシング)に最もよく一致するかによって異なります。


1
同様のアルゴリズムでそれらを実装できます。DFSにはスタックを使用し、BFSにはキューを使用します。BFSの問題は、これまでに確認されたすべてのノードを追跡する必要があることです。物理学のDFS ..私は代替の宇宙を想像し、あなたは生命のある宇宙を望み、根のすべての子供は異なるビッグバンであり、あなたは宇宙の死に至るまでずっと行き、生命はありませんか?最後の分岐点に戻り、すべてが使い果たされるまで次のターンを試み、次のビッグバンに進み、新しい宇宙の新しい物理法則を設定します。超直感的。良い問題は、チェス盤で馬との道を見つけることです。
juanmf 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.