在Java

时间:2019-01-08 15:53:41

标签: java multidimensional-array tree

我试图在表中显示树形结构,但我无法完全理解最后一点。

我有一个相当标准的节点类(为清晰起见,删除了方法和构造函数)

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"]]

我正在努力的是一种确定是否应打印(重复)某个值的方法,因为我确定必须有一个优雅的解决方案。

不能保证树的大小或结构,因此最通用的解决方案是最好的。任何帮助将不胜感激!

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

我不认为这是最好的解决方案,而且它似乎也不是很有效(必须删除最后添加的空行),但目前仍然有效。