Construct a tree structure from list of string paths
我在列表中有一个字符串路径集合,例如[" x1 / x2 / x3"," x1 / x2 / x4"," x1 / x5"]。
我需要从该列表构造一个树状结构,可以对其进行迭代以获得漂亮的打印树。
像这样
1 2 3 4 5 | x1 / \\ x5 x2 / \\ x3 x4 |
有什么想法/建议吗?
我认为可以通过处理字符串列表来首先解决该问题。编辑:选择的正确答案是一种优雅的实现,其他建议也很好。
遵循可访问树的朴素实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Tree< T > implements Visitable< T > { // NB: LinkedHashSet preserves insertion order private final Set<Tree> children = new LinkedHashSet<Tree>(); private final T data; Tree(T data) { this.data = data; } void accept(Visitor< T > visitor) { visitor.visitData(this, data); for (Tree child : children) { Visitor< T > childVisitor = visitor.visitTree(child); child.accept(childVisitor); } } Tree child(T data) { for (Tree child: children ) { if (child.data.equals(data)) { return child; } } return child(new Tree(data)); } Tree child(Tree< T > child) { children.add(child); return child; } } |
访问者模式的界面:
1 2 3 4 5 6 7 8 9 10 11 | interface Visitor< T > { Visitor< T > visitTree(Tree< T > tree); void visitData(Tree< T > parent, T data); } interface Visitable< T > { void accept(Visitor< T > visitor); } |
访问者模式的示例实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class PrintIndentedVisitor implements Visitor<String> { private final int indent; PrintIndentedVisitor(int indent) { this.indent = indent; } Visitor<String> visitTree(Tree<String> tree) { return new IndentVisitor(indent + 2); } void visitData(Tree<String> parent, String data) { for (int i = 0; i < indent; i++) { // TODO: naive implementation System.out.print(""); } System.out.println(data); } } |
最后(!!!)一个简单的测试用例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Tree<String> forest = new Tree<String>("forest"); Tree<String> current = forest; for (String tree : Arrays.asList("x1/x2/x3","x1/x2/x4","x1/x5")) { Tree<String> root = current; for (String data : tree.split("/")) { current = current.child(data); } current = root; } forest.accept(new PrintIndentedVisitor(0)); |
输出:
1 2 3 4 5 6 | forest x1 x2 x3 x4 x5 |
只需用其定界符分隔每个路径,然后将它们逐个添加到树结构中。
即如果
我会一次把树做成一串。
制作一棵空树(它有一个根节点-我认为可能存在类似" x7 / x8 / x9"的路径)。
采用第一个字符串,将x1添加到根节点,然后将x2添加到x1,然后将x3添加到x2。
获取第二个字符串,看到x1和x2已经存在,将x4添加到x2。
对您拥有的每条路径执行此操作。
创建一个对象节点,其中包含一个父节点(节点)和一个子节点列表(节点)。
首先使用","分割字符串。对于每个分割的字符串,您都使用" /"分割字符串。
在根列表中搜索第一个节点标识符(例如x1)。
如果可以找到它,请使用该节点查找下一个节点标识符(例如x2)。
如果找不到节点,则将该节点添加到在现有列表中可以找到的最后一个节点。
创建列表结构后,可以将列表打印到屏幕上。我将使其递归。
未测试,仅是动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public void print(List nodes, int deep) { if (nodes == null || nodes.isEmpty()) { return; } StringBuffer buffer = new StringBuffer(); for (int i = 0; i < deep; i++) { buffer.append("---"); } for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { Node node = (Node)iterator.next(); System.out.println(buffer.toString() +"" + node.getIdentifier()); print(node.getChildren(), deep + 1); } } |
这是我从路径(文件夹)结构中做树的方式。也许应该以基本逻辑帮助某人。
节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class Node { private String path; private List<Node> children; public Node(String path) { this.path = path; children = new ArrayList<>(); } public String getName() { return getName(path); } private String getName(String path) { String[] split = path.split("\\\\\"); return split[split.length - 1]; } public void addChild(Node child) { children.add(child); } public List<Node> getChildren() { return children; } public String getPath() { return path; } } |
FilesTree:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class FilesTree { private static final Logger log = Logger.getLogger(FilesTree.class.getName()); private FilesTree() {} private static void createTree(Node root, List<String> paths) { for (String path : paths) { addNode(root, Arrays.asList(path.split("\\\\\")),""); } } private static void addNode(Node node, List<String> path, String nodePath) { if (!path.isEmpty()) { nodePath = nodePath.equals("") ? path.get(0) : String.format("%s\\\\%s", nodePath, path.get(0)); } if (node.getChildren().isEmpty() && path.size() == 1) { node.addChild(new Node(nodePath)); } else if (!node.getChildren().isEmpty()) { for (Node actual : node.getChildren()) { if (actual.getName().equals(path.get(0))) { addNode(actual, path.subList(1, path.size()), nodePath); return; } } node.addChild(new Node(nodePath)); } else { log.info("Without children but with size:" + path.size()); } } } |
为数组中的每个字符串创建树。
只需为'/'分割路径,检查树中是否存在该节点,如果存在则继续前进...否则,创建一个新节点并将该节点添加到父节点的子节点中。
使用递归进行迭代。
以下是树节点的模型。
1 2 3 4 5 6 7 8 9 | Class Node{ string name; List<Node> childrens; Node(string name){ this.name = name; this.childrens = new List<Node>(); } } |