遍历任意树结构中的每个唯一路径(从根到叶)

时间:2011-04-17 06:21:45

标签: algorithm data-structures tree

我有几个清单:

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)已访问过的树的叶子。

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

4 个答案:

答案 0 :(得分:10)

行。我认为你的意思是你想要找到从根到叶子的每条路径。

然后(未优化的版本)

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));
    }
  }
}

答案 1 :(得分:2)

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

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

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

这将遍历所有孩子,你几乎可以删除node.isLeaf案例,因为回溯是自动完成的。请注意,我编写了nextChild函数,因为我无法在代码中看到它的内容,但是你必须有类似的东西,或者某种方式来遍历子代。

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

答案 2 :(得分:1)

我想出了一些在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

答案 3 :(得分:0)

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);
    }

printPath(根);