我正在构建一个哈希列表,表示树中的根节点路径。我的功能有效,但它们在大型树形结构上的速度非常慢 - 有更好的方法吗?我尝试在一个函数中构建列表,但是我得到了我不想要它们的独特哈希值。
public ArrayList<Integer> makePathList(AbstractTree<String> tree){
StringBuilder buffer = new StringBuilder();
ArrayList<Integer> pl = new ArrayList<Integer>();
ArrayList<StringBuilder> paths = getPaths(tree, buffer);
for(StringBuilder sb : paths){
pl.add(sb.toString().hashCode());
}
return pl;
}
public ArrayList<StringBuilder> getPaths(AbstractTree<String> tree, StringBuilder parent){
ArrayList<StringBuilder> list = new ArrayList<StringBuilder>();
parent.append("/");
parent.append(tree.getNodeName());
list.add(new StringBuilder(parent));
if (!tree.isLeaf()){
int i = 0;
Iterator<AbstractTree<String>> child = tree.getChildren().iterator();
while (i < tree.getChildren().size()){
list.addAll(getPaths(child.next(), new StringBuilder(parent)));
i++;
}
}
return list;
}
更新:
Marcin建议在树遍历期间制作哈希给出了错误的答案,但也许这就是我这样做的方式?
public ArrayList<Integer> getPaths(AbstractTree<String> tree, StringBuilder parent){
ArrayList<Integer> list = new ArrayList<Integer>();
parent.append("/");
parent.append(tree.getNodeName());
list.add(new StringBuilder(parent).toString().hashCode());
if (!tree.isLeaf()){
int i = 0;
Iterator<AbstractTree<String>> child = tree.getChildren().iterator();
while (i < tree.getChildren().size()){
list.addAll(getPaths(child.next(), new StringBuilder(parent)));
i++;
}
}
return list;
}
答案 0 :(得分:1)
我认为您的主要问题是您生成的重复数据量:对于树的每个叶子,您将复制通向该叶子的整个路径并计算该路径的哈希值。即如果在一个顶级节点下有50,000个叶子,那么该节点的路径名将被复制50,000次,其哈希值将被计算50,000次。
如果您可以组织数据以便重用共享路径前缀作为叶子之间的引用,并且缓存和重用这些前缀的哈希计算,则可以大幅减少实际完成的工作量。
答案 1 :(得分:0)
jvisualvm在哪里表明性能瓶颈是什么?
答案 2 :(得分:0)
首先创建一个包含所有路径的列表,然后在拥有它们之后,计算所有路径的哈希值。所有这些路径的列表大小是O(n ^ 3)(有O(n ^ 2)路径,每个O(n)长)为什么?为什么不在遍历树时计算哈希值?通过这种方式,您可以从时间复杂度中获取整个 n 。
正确解决方案的代码(结果以传入的整数列表结束):
public void getPaths(AbstractTree<String> tree, StringBuilder parentPath,
List<Integer> list)
StringBuilder newPath = parentPath.clone();
newPath.append("/");
newPath.append(tree.getNodeName());
list.add(newPath.toString().hashCode());
if (!tree.isLeaf()){
Iterator<AbstractTree<String>> child = tree.getChildren().iterator();
for (AbstractTree<String> child : tree.getChildren()){
getPaths(child, newPath, list)
}
}
}
这仍然是O(n ^ 2)。这是因为对O(n ^ 2)值的字符串进行散列(每个节点的路径长度与其深度成比例),如果你有一个只有给定节点的散列,你甚至可以把它降低到O(N)其父母路径的哈希并以某种方式修改它。
优化包括: - 并行树遍历 - 使用更智能的散列(即子节点的散列是父路径的子节点和散列的函数,而不是整个父节点)。
答案 3 :(得分:0)
我认为复杂性仍然是一样的。无论您是使用内联创建哈希(O(n ^ 2))还是在递归后执行(O(n ^ 2 + n)= O(n ^ 2))。 找到快捷方式的唯一机会是在另一个地方做一些工作。例如您可以在插入节点时对路径进行散列,并仅在另一个点收集所有散列。