我试图在表中显示树形结构,但我无法完全理解最后一点。
我有一个相当标准的节点类(为清晰起见,删除了方法和构造函数)
public class TreeNode<T> {
private TreeNode<?> parent;
private T data;
private List<TreeNode<?>> children = new ArrayList<>();
}
其中可能填充了以下内容:
TreeNode<String> grandchild1 = new TreeNode<>("Grandchild 1");
TreeNode<String> grandchild2 = new TreeNode<>("Grandchild 2");
TreeNode<String> grandchild3 = new TreeNode<>("Grandchild 3");
TreeNode<String> child1 = new TreeNode<>("Child 1");
child1.addChild(grandchild1);
child1.addChild(grandchild2);
TreeNode<String> child2 = new TreeNode<>("Child 2");
child2.addChild(grandchild3);
TreeNode<String> root = new TreeNode<>("Root");
root.add(child1);
root.add(child2);
为了在表中打印此数据,我想填充一个嵌套的List结构,如下所示,
[["Root", "Child 1", "Grandchild 1"],
["", "", "Grandchild 2"],
["", "Child 2", "Grandchild 3"]]
以便数据被“展平”,并且不会重复,因此稍后我将能够在这样的表中打印数据:
| Root | Child 1 | Grandchild 1 |
| | | Grandchild 2 |
| | Child 2 | Grandchild 3 |
这是我到目前为止尝试过的:
public List<List<String>> getData(TreeNode<?> root) {
List<List<String>> data = new ArrayList<>();
getData(data, root);
return data;
}
private void getData(List<List<String>> data, TreeNode<?> node) {
if (!node.isLeaf()) {
for (TreeNode<?> child : node.getChildren()) {
getData(data, child);
}
} else {
data.add(getRow(currentNode));
}
}
private List<String> getRow(TreeNode<?> node) {
Deque<String> row = new LinkedList<>();
TreeNode<?> currentNode = node;
while (!currentNode.isRoot()) {
row.addFirst(String.valueOf(currentNode.getData()));
currentNode = currentNode.getParent();
}
return new ArrayList<>(row);
}
尽管这会递归地收集要显示的数据,但显然,对于打印其父值的每一行,数据都会重复出现,而我将得出以下结论:
[["Root", "Child 1", "Grandchild 1"],
["Root", "Child 1", "Grandchild 2"],
["Root", "Child 2", "Grandchild 3"]]
我正在努力的是一种确定是否应打印(重复)某个值的方法,因为我确定必须有一个优雅的解决方案。
不能保证树的大小或结构,因此最通用的解决方案是最好的。任何帮助将不胜感激!
答案 0 :(得分:1)
遍历与@Matteo Ugolotti的answer相同,只是拖尾深度和前导节点打印不同。
对于每个节点,将所有子级递归打印在同一行上。打印该行时,也会递归打印刚刚打印的节点的每个子节点(如果有)。
这些列有一个max列,用于将所有节点打印为直列。它应该至少与最大的节点数据一样大。
我删除了泛型,并使每个节点var array = [{value:"a"},{value:"b"},{value:"c"},{value:"a"},{value:"c"},{value:"d"}];
console.log(array.filter((obj, pos, arr) => {
return arr.map(mapObj => mapObj["value"]).indexOf(obj["value"]) === pos;
}));
能够获得每个节点的长度(用于正确打印列)。您可以在Node类中创建一个返回(通用)数据长度的方法。
String
答案 1 :(得分:0)
可能您可以通过对树的简单DFS探索来实现。跟踪树的当前深度,然后在每个节点上打印深度乘以白色列,然后是带有节点值的列。
public void printTree(TreeNode<T> node, int depth) {
// Print leaf
if(node.children.size() == 0) {
System.out.print(node.data);
System.out.print("\n");
return;
} else {
// Print current node
System.out.print(node.data);
// Print next nodes at this level
printTree(node.children.get(0), depth + 1);
// Print remaining nodes with leading white columns
for (int i = 1; i < node.children.size(); i++) {
for (int j = 0; j < depth; j++) {
System.out.print(" |");
}
System.out.print(node.children.get(i);
System.out.print("\n");
}
}
}
答案 2 :(得分:0)
由于P. van der Laan,我得以提出一个相当不优雅的解决方案。如果有更好或更多的“标准”方式实现这一目标,我很想听听!
所以我做了一个类来保存嵌套列表和当前行位置:
public class NodeGrid {
private List<List<String>> data = new ArrayList<>();
private int currentRow = 0;
public void addData(String dataString) {
while (data.size() <= currentRow) {
data.add(new ArrayList<>());
}
data.get(currentRow).add(dataString);
}
public void newRow() {
data.add(new ArrayList<>());
currentRow++;
}
public List<List<String>> getData() {
data.remove(currentRow); // the last row is deleted before retrieving since it will always be empty
return data;
}
}
然后可以用相同的方式实现DFS:
private void getRowData(NodeGrid data, TreeNode<?> currentNode, int depth) {
if (currentNode.isLeaf()) {
data.addData(String.valueOf(currentNode.getData()));
data.newRow();
} else {
data.addData(String.valueOf(currentNode.getData()));
getRowData(data, currentNode.getChild(0), depth + 1);
for (int i = 1; i < currentNode.getChildren().size(); i++) {
for (int d = 0; d < depth + 1; d++) {
data.addData("");
}
getRowData(data, currentNode.getChild(i), depth + 1);
}
}
}
我不认为这是最好的解决方案,而且它似乎也不是很有效(必须删除最后添加的空行),但目前仍然有效。