【发布时间】:2021-09-07 07:01:08
【问题描述】:
我目前正在研究 N-ary 树,我偶然发现了 Level Order Traversal。理论上它看起来很容易,在代码上运行并不难,但现在我想加强它并添加递归,这样我就可以更好地理解它。事情是我现在发现这样做非常困难。有我的代码:
- The node class
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of a generic tree node containing the data and a list of children.
*
* @param <T> Generic type meant to implement reference types into the tree.
*/
public class Node<T> {
private T data;
private List<Node<T>> children;
/**
* Constructor that initializes the data and the list of children of the current node.
*
* @param data The value of the node.
*/
public Node(T data) {
this.data = data;
children = new ArrayList<>();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public List<Node<T>> getChildren() {
return children;
}
public void setChildren(List<Node<T>> children) {
this.children = children;
}
}
-The tree class
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/** Implementation of a generic n-ary tree. */
public class Tree<T> {
private Node root;
private List<Node<T>> nodes;
/**
* Constructor that initializes the root node of the tree.
*
* @param data The value of the root node.
*/
public Tree(T data) {
root = new Node<>(data);
}
public Node getRoot() {
return root;
}
/**
* Method that implements the Level Order Traversal algorithm. It's a left to right traverse where
* each level of the tree is being printed out. First the root , then it's children and then each
* child's children etc.
*
* @param root The root node of a tree.
*/
public String levelOrderTraversal(Node root) {
StringBuilder result = new StringBuilder();
if (root == null) {
return "";
}
result.append("\n");
Queue<Node> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()) {
int queueSize = q.size();
while (queueSize > 0) {
Node node = q.peek();
q.remove();
result.append(node.getData().toString()).append(" ");
for (int i = 0; i < node.getChildren().size(); i++) {
q.add((Node) node.getChildren().get(i));
}
queueSize--;
}
result.append("\n");
}
return result.toString();
}
/**
* This method serves to recursively move through and retrieve the nodes, so they can be printed
* out to the user.
*
* @param root The root node of the tree.
*/
private void walkThroughElements(Node root) {
if (root == null) {
return;
}
nodes.add(root);
for (Object node : root.getChildren()) {
walkThroughElements((Node) node);
}
}
/**
* Implementation of pre-order traversal of a generic tree. This traversal visit the root node
* first , prints it , then visits the whole left sub-tree (the list of every child node), prints
* every node and then traverses the right sub-tree , prints the nodes and ends the algorithm.
*
* @param root The root node of the tree.
* @return The nodes of the tree as a string.
*/
private String preOrderTraversal(Node<T> root) {
nodes = new ArrayList<>();
StringBuilder result = new StringBuilder();
walkThroughElements(root);
for (Node node : nodes) {
result.append(node.getData()).append(" ");
}
result.setLength(result.length() - 1);
return result.toString();
}
public String preOrderTraversal() {
return preOrderTraversal(root);
}
}
有没有一种有效的方法,或者递归地运行这个级别顺序遍历方法是否有意义?
这是修改后的关卡顺序代码
public String levelOrderTraversal(Node root) {
StringBuilder result = new StringBuilder();
if (root == null) {
return "";
}
result.append("\n");
Queue<Node> q = new LinkedList<>();
q.add(root);
collectNodes(root, root.getChildren());
result.append(root.getData().toString()).append(" ");
result.append("\n");
return result.toString();
}
它在调用 collectNodes 的那一行给出错误。
这就是 collectNodes() 的样子。
private void collectNodes(Node<T> node, List<Node<T>> nodes) {
nodes.add(node);
for (Object child : node.getChildren()) {
collectNodes((Node) child, nodes);
}
}
【问题讨论】:
-
我假设您在问将非递归
levelOrderTraversal()转换为递归是否有意义?当然,它是有道理的,而且可能更容易阅读。在您真正遇到问题之前,我不会担心效率,这里的差异不应该那么大。请注意,您的递归walkThroughElements()可以通过在递归调用该方法之前首先将所有子项添加到列表中来转换为“级别顺序”或“广度优先”。 -
@Thomas 是的,完全正确!但是我无法真正完成将 walkThroughElements() 转换为 level-order 的整个过程,您介意稍微扩展您的解释吗,因为不幸的是我的递归速度有点慢。 :)
-
您正在调用
collectNodes(root, root.getChildren());,它使用子列表作为结果,因此您正在迭代该列表并通过添加元素来修改它。相反,创建一个新列表并将其传递给该调用,然后从该新列表构建您的结果字符串,即List<Node<T>> collectedNodes = new ArrayList<>(); collectNodes(root, collectedNodes ); /*now use collectedNodes to build the string*/。 -
@Thomas 是的,这似乎有效,但有点像 Pre-order ,我想我现在必须环顾四周才能解决这个问题
-
是的,您的代码将是深度优先的方法。如果你想要广度优先,你需要先添加子节点(并且只添加子节点,而不是节点本身),然后进行递归调用。
标签: java algorithm tree-traversal