web-dev-qa-db-ja.com

再帰を使用せずにn-aryツリーをトラバースする

再帰を使用せずにn-aryツリーをトラバースするにはどうすればよいですか?

再帰的な方法:

traverse(Node node)
{
    if(node == null)
        return;

    for(Node child : node.getChilds()) {
        traverse(child);
    }
}
19
ako

これは、再帰やスタックなしで実行できます。ただし、ノードに2つのポインタを追加する必要があります。

  1. 親ノード。ですから、終わったら親に戻ることができます。
  2. 現在の子ノードなので、次に取るノードがわかります。

    • ノードごとに、すべての子を処理します。
    • 子供が処理される場合は、次の子供がいるかどうかを確認し、それを処理します(現在の子を更新します)。
    • すべての子供が処理されたら、親に戻ります。
    • ノードがNULLの場合は、終了します。

擬似コードを使用すると、次のようになります。

traverse(Node node) {
  while (node) {
    if (node->current <= MAX_CHILD) {
      Node prev = node;
      if (node->child[node->current]) {
        node = node->child[node->current];
      }
      prev->current++;
    } else {
      // Do your thing with the node.
      node->current = 0; // Reset counter for next traversal.
      node = node->parent;
    }
  }
}
10
Toon Krijthe

あなたがしているのは本質的にツリーのDFSです。スタックを使用すると、再帰を排除できます。

traverse(Node node) {
    if (node==NULL)
        return;

    stack<Node> stk;
    stk.Push(node);

    while (!stk.empty()) {
        Node top = stk.pop();
        for (Node child in top.getChildren()) {
            stk.Push(child);
        }
        process(top);
    }
}

BFSが必要な場合は、キューを使用します。

traverse(Node node) {
    if (node==NULL)
        return;

    queue<Node> que;
    que.addRear(node);

    while (!que.empty()) {
        Node front = que.deleteFront();
        for (Node child in front.getChildren()) {
            que.addRear(child);
        }
        process(front);
    }
}

他の方法でトラバースする場合は、ノードを格納するためのデータ構造が異なりますが、同じアプローチに従う必要があります。おそらく優先度付きキュー(各ノードで関数を評価し、その値に基づいてノードを処理する場合)。

21
BiGYaN

言語が指定されていないため、疑似擬似コードでは次のようになります。

traverse(Node node)
{
  List<Node> nodes = [node];

  while (nodes.notEmpty) {
    Node n = nodes.shift();

    for (Node child in n.getChildren()) {
      nodes.add(child);
    }

    // do stuff with n, maybe
  }
}

これは、質問で指定された深さ優先トラバーサルとは対照的に、幅優先トラバーサルであることに注意してください。最初のアイテムをpopする代わりに、nodesリストの最後のアイテムをshiftすることで、深さ優先探索を実行できるはずです。

9