我目前正在研究N进制树,我偶然发现了级序遍历。理论上看起来很简单,在代码上运行并不困难,但是现在我想把它升级并添加递归,这样我就可以更好地理解它。问题是我现在发现很难这样做。这是我的代码:
- 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);
}
}
正确的方法是以懒惰的方式去做
// assume not null Node::data (if so, wrap into Optional or similar)
static <T> Stream<T> levelOrderTraversal(Node<T> tree) {
final List<Node<T>> fifo = new ArrayList<>();
if(tree != null)
fifo.add(tree);
final Function<T, T> next = o -> {
if(fifo.isEmpty())
return null; // End Of Stream
Node<T> x = fifo.remove(0);
System.out.println("++++++ " + x.data);
if(x.children != null)
fifo.addAll(x.children);
return x.data;
};
return Stream.iterate(null, next::apply).skip(1).takeWhile(Objects::nonNull);
}
例如
++++++ foo
foo
++++++ bar1
bar1
++++++ bar2
bar2
++++++ par11
par11
++++++ par12
par12
++++++ par21
par21
++++++ par22
par22
++++++ subA
subA
++++++ subB
subB
只控制中间的遍历级别(而不是整个树)。
例如,如果记录每个节点扩展并过滤树,则不会记录所有节点
levelOrderTraversal(tree)
.takeWhile(x -> !x.equals("bar2"))
.forEach(System.out::println);
只展开foo
,bar1
和bar2
节点
++++++ foo
foo
++++++ bar1
bar1
++++++ bar2
使用递归通常会比迭代慢,并且使用更多的堆栈空间。但是,是的,这也可以用递归方法(DFS方法)来解决。
供参考:https://leetcode.com/problems/binary-tree-level-order-traversal/discuss/33562/Java-1ms-DFS-recursive-solution-and-2ms-BFS-iterative-solution
您可以使用迭代(例如通过堆栈)或递归来解决这些迭代问题。让我们使用一个方法来收集节点,非常类似于您的walkousElements()
:
深度优先
//add the node and then go deeper
void collect(Node node, Collection<Node> nodes) {
nodes.add(node);
for(Node child : node.getChildren()) {
collect(child, nodes);
}
}
迭代
class Level {
final Node node;
Iterator<Node> childItr;
//constructor, setters, getters
}
void collect(Collection<Node> nodes) {
Stack<Level> levels = new Stack<>();
nodes.add(root);
levels.push(new Level(root, root.getChildren().iterator()));
while( !levels.isEmpty() ) {
Level currentLevel = levels.peek();
//remove the current level as it doesn't have any more children
if( !currentLevel.childItr.hasNext() ) {
levels.pop();
} else {
//get the next child and add it to the result
Node child = currentLevel.childItr.next();
nodes.add(child);
//go down to the child's level
levels.push(new Level(child, child.getChildren().iterator())
}
}
}
广度优先
递归
//add the children first (i.e. the entire level) and then go deeper
void collectChildren(Node node, Collection<Node> nodes) {
for(Node child : node.getChildren()) {
nodes.add(child);
collectChildren(child, nodes);
}
}
//special case: root node
void collect(Collection<Node> nodes) {
nodes.add(root);
collectChildren(root, nodes);
}
迭代
void collect(Collection<Node> nodes) {
Queue<Node> nodesToProcess = new LinkedList<>();
nodesToProcess.add(root);
while( !nodesToProcess.isEmpty() ) {
Node node = nodesToProcess.poll();
nodes.add(node);
nodesToProcess.addAll(node.getChildren());
}
}
正如您所看到的,递归在深度优先上比广度优先更容易,但无论如何都很容易阅读。递归将使用调用堆栈来维护状态,因此它会占用(非堆)内存,并且对其深度也有限制(取决于内存量,但臭名昭著的StackOverflowException会告诉您存在错误或树太深)。
宽度优先的迭代更容易,并且需要额外的构造,如堆栈或队列。对于这些结构,它需要一些堆内存,并且可能由于一些优化而更快,但一般来说,我不会在这里讨论性能差异,因为它们应该只在真正大的树上显示自己——在这种情况下,递归可能已经达到了调用堆栈的限制。
(为了避免冗长的解释,我所要寻找的只是java中泛型树(n元树)的级别顺序遍历。提供的代码正常工作,需要级别顺序显示功能。环顾四周一个小时,但找不到通用n元树的参考。如果soemone能帮助我在代码上构建LevelOrderDisplay函数,我将不胜感激,因为它将帮助我理解我遇到的队列错误。谢谢 我一直在尝试在工作中实现Autosys作业计划的树表示。由于每个作业(流程)可以有一个或多个依赖作业
我需要执行一个三元树的预购遍历。我很熟悉二叉树上的这种遍历,比如: 它按根、左、右顺序遍历。我很困惑如何在添加了中间子节点的情况下做到这一点。如果有人能解释这个,那就太好了。谢谢
所以我在研究树遍历算法。例如,在K-d树遍历中,我们的目标是遍历节点直至叶子。这与其说是一个树搜索,不如说是一个根到叶的遍历。 在这种情况下,递归解决方案就足够了。但是,在C等语言中,递归调用函数需要将值推送到堆栈上,并在堆栈帧之间跳跃等。标准的递归方法类似于: 因此,考虑到二叉树有一个明确的上界(我相信这也可以扩展到其他树类型),以迭代方式执行此遍历是否更有效: 二叉树的最大高度是它的节点数,而
我试图使用队列的链表实现实现二叉搜索树的级别顺序遍历。 我已经检查了二叉查找树的实现,它是好的。队列的链表实现也是正确的。在这里,我试图访问节点并将其子节点排队。然后使用弹出函数实际访问节点。 这最终是通过递归调用完成的。当我运行以下代码时,我以不同的顺序获得输出。
我想逐级显示树结构。我当前的代码执行BFS或级别顺序遍历,但我无法使输出像树一样显示树结构。请参阅当前输出和预期输出。 我的想法是使用某种计数来迭代队列中同一级别的元素。 我怎么能这样做呢。 没有此功能的原始代码可以在下面的链接中找到,以防有人需要整个实现,否则只需查看下面的显示BFS功能。 java中泛型树(n元树)的级顺序遍历 谢谢 另外,我之前发布了一个具有相同功能的逻辑问题,因为已经回答了
通常为二叉树定义顺序。让我们假设顺序对于(普通)树是“扩展”的。如果树是单个节点,则该节点是树的顺序遍历。如果树T是具有T1,T2,…,的树。。。,Tk子树和r作为根,然后按T1的顺序,然后按r的顺序,然后按T2,T3,…,的顺序遍历。。,Tk是T的顺序遍历。 T以左子右同级表示形式给出。C中节点的数据结构为: 树用指向根的指针表示。根不能有正确的同级<问题是要找到一种算法来按顺序遍历树。不允许递