【问题标题】:Traverse every unique path (from root to leaf) in an arbitrary tree structure遍历任意树结构中的每条唯一路径(从根到叶)
【发布时间】:2011-04-17 06:21:44
【问题描述】:

我有几个列表:

A = ["a0", "a1"]       // the number of lists varies
B = ["b0", "b1", "b2"] // such as the number of elements in a list.
C = ["c1"]
D = ["d0", "d1"]

我把这个结构转换成树:

             _____ROOT______
            /               \ 
       ___a0____        ____a1____
      /    |    \      /     |    \
    b0    b1    b2    b0    b1    b2
     |     |     |     |     |     |
    c1    c1    c1    c1    c1    c1
   / |   / |   / |   / |   / |   / |
  d0 d1 d0 d1 d0 d1 d0 d1 d0 d1 d0 d1

我正在打印树中的每条唯一路径(省略根):

a0 -> b0 -> c1 -> d0
a0 -> b0 -> c1 -> d1
a0 -> b1 -> c1 -> d0
...
a1 -> b2 -> c1 -> d1

我通过以下方式遍历树时“破坏”树本身来做到这一点:

public static void delete(Node node) {
  if (node.isLeaf() && !node.isRoot()) {
    Node parent = node.getParent();
    parent.removeChild(node);
    delete(parent);
  }
}

public static void traverse(Node node) {
  if (node.isRoot())
    System.out.println("---");
  else
    System.out.println(node.getName());

  if (node.isLeaf()) {    // I'm still working on
    if (!node.isRoot()) { // removing unnecessary checks
      delete(node);
      traverse(node.getRoot());
    }
  } else {
    Node child = node.firstChild();
    if (null != child)
      traverse(child);
  }
}      

traverse(Node) 总是打印树的第一个可用路径(从根到叶),而delete(Node) 切割已经被traverse(Node) 访问过的树的叶。

这按预期工作,但我很想找到一种解决方案,以前面描述的方式遍历树而不破坏它。 如果有办法做到这一点,那么我有兴趣遍历这个相同的结构,但以图形的形式来减少冗余。

【问题讨论】:

    标签: algorithm data-structures tree


    【解决方案1】:

    好的。我认为您实际上的意思是要找到从根到叶子的每条路径。

    然后(未优化的版本)

    void traverse (Node root) {
      // assume root != NULL
      traverse (root, new LinkedList<Node>());
    }
    
    private void traverse (Node root, LinkedList<Node> path) {
      path.add(root);
      if (root.isLeaf()) {
        print path;
      }
      else {
        for each node of root {
          traverse (node, new LinkedList<Node>(path));
        }
      }
    }
    

    【讨论】:

    • 你怎么能用javascript做到这一点?
    • 添加小代码以使函数返回所有路径。无论如何谢谢。 :)
    【解决方案2】:

    所以基本上你是在进行深度优先搜索,但不是以非破坏性的方式显式跟踪节点的访问,或者在不跟踪的情况下保持足够的上下文进行搜索,而是破坏树来进行跟踪。

    将其转换为普通 DFS 的传统方法是循环您的递归条件,基本上将子递归调用更改为:

    } else {
      for (Node child = node.firstChild(); node != null; node = node.nextChild()) {
          traverse(child);
      }
    }
    

    这将遍历您所有的孩子,并且您几乎可以删除 node.isLeaf 案例,因为回溯会自动为您完成。请注意,我编造了 nextChild 函数,因为我看不到它在您的代码中调用了什么,但您必须有类似的东西,或者某种方式来遍历子项。

    保留更多现有代码结构的另一种方法是维护一个单独的数据结构,其中包含一组“已访问”节点,如果所有节点名称都是唯一 - 而不是删除节点,将其添加到“已访问”集合中,并且在您的递归条件中,不要检查 null,而是找到第一个未访问的节点。这可能比上面的建议更复杂,但可能更类似于您现在所拥有的 - 并且在您需要在循环图而不是树上执行此操作的情况下避免循环。

    【讨论】:

    • 除此之外,您可能还需要阅读广度优先搜索和深度优先搜索。
    • 如果我要破坏我的树(或其副本),sn-p 中提出的解决方案可以工作,但这不是我的意图。第二个想法确实是我现在看到的最好的解决方案,但是姜丹特为此提出了一个优雅的解决方案,这就是为什么我不能接受你的答案。还是谢谢!
    • FWIW,sn-p 中的解决方案不打算用于破坏树,正如 cmets 紧随其后解释的那样 - 实际上与 Dante Jiang 提出的解决方案相同。
    【解决方案3】:

    我想出的东西是在 TrieTree 中打印单词,可以很容易地适应其他种类的树或不同的需求:

    public void rootToLeaves() {
        HashMap<Integer, TrieNode> hashMap = new HashMap<Integer, TrieNode>();
        for(TrieNode trieNode : root.getChildren())
            rootToLeaves(trieNode, hashMap, 0);
    }
    
    private void rootToLeaves( TrieNode trieNode, HashMap<Integer, TrieNode> hashMap, int heightIndex ) {
        hashMap.put(heightIndex, trieNode);
    
        if( trieNode.isLeaf() )
            printValues(hashMap, heightIndex);
        else
            for( TrieNode childNode : trieNode.getChildren() )
                rootToLeaves( childNode, hashMap, heightIndex + 1 );
    }
    
    private void printValues(HashMap<Integer, TrieNode> hashMap, int heightIndex) {
        for(int index = 0; index <= heightIndex; index++)
            System.out.print(hashMap.get(index).getValue());
        System.out.println();
    }
    

    这个解决方案在内存管理方面做得很好(它使用单个HashMap,其大小永远不会超过树的高度)并且它提供了很大的灵活性(只需将 printValues 替换为您需要的任何内容)。

    注意:提前知道树的高度可以让您使用简单的Array 而不是Map

    【讨论】:

      【解决方案4】:

      1、寻找叶节点
      2、从叶子节点向上遍历

      public void printPath(N n) {
              if (n == null)
                  return;
              if (n.left == null && n.right == null) {
                  do {
                      System.out.print(n.value);
                      System.out.print(" ");
                  } while ((n = n.parent) != null);
                  System.out.println("");
                  return;
              }
              printPath(n.left);
              printPath(n.right);
          }
      

      打印路径(根);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-24
        • 2014-12-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多