web-dev-qa-db-ja.com

JavascriptのみのDOMツリートラバーサル-DFSとBFS?

誰かがコード、擬似コード、またはプレーンJavaScript(JQueryやヘルパーライブラリなし)でDFSとBFSを実装するための適切なリンクを提供できますか?どちらのトラバーサルを実装するかを理解しようとしていますが、BFSとDFSの実装の違いを実際に区別できないようです。

例として具体的な問題が必要な場合:特定のノードでDOMをトラバースし、すべてのクラス名を取得します。

(トラバースすることを考える唯一の方法は、各親ノードを通過し、そのノードから必要なものを取得することです。この例ではクラス名です。次に、子があるかどうかを確認し、子ごとに繰り返します。これはDFSだと思います。繰り返しますが、DOMトラバーサル実装の違いを理解するのに苦労しています!)

最後に、これが繰り返しの場合は申し訳ありません。私はどこでも良い、明確な例を探しましたが、素晴らしい答えは見つかりませんでした!そこにすでに良い答えがある場合は、私に知らせてください:)

13
pm3

例として次のHTMLコードを使用してみましょう。

<div class="a">
    <div class="aa">
        <span class="aaa">
        </span>
        <span class="aab">
        </span>
    </div>
    <div class="ab">
        <span class="aba">
        </span>
        <span class="abb">
        </span>
    </div>
</div>

DFSは常に最初に次のレベルのノードに移動し、トラバースされていない子ノードがなくなった場合にのみ、現在のレベルの次のノードに移動します。

DFSは、次の順序で例のノードをトラバースします。

a, aa, aaa, aab, ab, aba, abb

BFSは常に最初に現在のレベルのすべてのノードをトラバースし、その後でのみ次のレベルのノードに移動します。

BFSは、次の順序で例のノードをトラバースします。

a, aa, ab, aaa, aab, aba, abb

これらのどれを使用すべきか明確な答えはありません。通常、それはあなたのニーズに依存します。

実装の詳細:

DFSの場合、人々はしばしば stack を使用します。

擬似コード:

stack my_stack;
list visited_nodes;
my_stack.Push(starting_node);

while my_stack.length > 0
   current_node = my_stack.pop();

   if current_node == null
       continue;
   if current_node in visited_nodes
      continue;
   visited_nodes.add(current_node);

   // visit node, get the class or whatever you need

   foreach child in current_node.children
      my_stack.Push(child);

このコードは、スタックにノードが存在するまで続きます。各ステップで、スタックの最上位ノードを取得します。それがnullでない場合、および以前にアクセスしたことがない場合は、アクセスしてすべての子をスタックに追加します。

Queue は通常BFSに使用されます。

擬似コード:

queue my_queue;
list visited_nodes;
my_queue.enqueue(starting_node);

while my_queue.length > 0
   current_node = my_queue.dequeue();

   if current_node == null
       continue;
   if current_node in visited_nodes
      continue;
   visited_nodes.add(current_node);

   // visit node, get the class or whatever you need

   foreach child in current_node.children
      my_queue.enqueue(child);

このコードは、キューにノードが存在するまで続きます。各ステップで、キューの最初のノードを取得します。それがnullでない場合、および以前にアクセスしたことがない場合は、アクセスしてすべての子をキューに追加します。

2つのアルゴリズムの主な違いは、使用するデータ型であることに注意してください。

10
Robert F.

DFS:

function m(elem) {
    elem.childNodes.forEach(function(a) {
        m(a);
    });
    //do sth with elem
}
m(document.body);

これはすべての要素をループし、各要素については各子などをループします。

BFS:

var childs = [];

function m(elem) {
    elem.childNodes.forEach(function(a) {
        childs.Push(a);
    });
    b = childs;
    childs = [];
    b.forEach(function(a) {
        m(a);
    });
}
m(document.body);

これは要素をループし、子をスタックにプッシュし、各要素から再開します。ご覧のとおり、これははるかに多くのスペース(子の配列)を消費しますが、これは最善ではありません...

4
Jonas Wilms

DFSの場合、 TreeWalker または NodeIterator APIを使用し、NodeFilter.SHOW_ELEMENTでフィルタリングできます。

4
the8472