Java 缓慢构建路径列表

Java 缓慢构建路径列表,java,optimization,hash,recursion,tree,Java,Optimization,Hash,Recursion,Tree,我正在构建一个表示树中根到节点路径的哈希列表。我的函数可以工作,但它们在大型树结构上的速度非常慢-有更好的方法吗?我曾尝试在一个函数中构建列表,但在我不需要它们的地方得到了唯一的哈希值 public ArrayList<Integer> makePathList(AbstractTree<String> tree){ StringBuilder buffer = new StringBuilder(); ArrayList<Integer> p

我正在构建一个表示树中根到节点路径的哈希列表。我的函数可以工作,但它们在大型树结构上的速度非常慢-有更好的方法吗?我曾尝试在一个函数中构建列表,但在我不需要它们的地方得到了唯一的哈希值

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;
}
publicArrayList生成路径列表(抽象树){
StringBuilder缓冲区=新的StringBuilder();
ArrayList pl=新的ArrayList();
ArrayList路径=获取路径(树、缓冲区);
用于(StringBuilder sb:路径){
pl.add(sb.toString().hashCode());
}
返回损益;
}
公共ArrayList GetPath(抽象树、StringBuilder父级){
ArrayList=新建ArrayList();
父项。追加(“/”);
append(tree.getNodeName());
添加(新的StringBuilder(父级));
如果(!tree.isLeaf()){
int i=0;
Iterator child=tree.getChildren().Iterator();
而(i
更新:

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;
}
public ArrayList getpath(抽象树,StringBuilder父级){
ArrayList=新建ArrayList();
父项。追加(“/”);
append(tree.getNodeName());
添加(新的StringBuilder(父).toString().hashCode());
如果(!tree.isLeaf()){
int i=0;
Iterator child=tree.getChildren().Iterator();
而(i
jvisualvm指出性能瓶颈在哪里?

我认为您的主要问题是生成的重复数据量:对于树的每一个叶,您将复制通向该叶的整个路径,并计算该路径的哈希值。i、 e.如果一个顶级节点下有50000个叶子,那么该节点的路径名将被复制50000次,其哈希计算50000次


如果您可以组织数据,以便将共享路径前缀重新用作叶之间的引用,并缓存和重新使用这些前缀的哈希计算,则可以大大减少实际需要完成的工作量。

首先创建所有路径的列表,然后在拥有所有路径后计算哈希。所有这些路径列表的大小都是O(n^3)(有O(n^2)条路径,每条路径都是O(n)长)为什么?为什么不在遍历树时计算散列呢?这样你就可以从你的时间复杂度中去掉整整一个n

正确解决方案的代码(结果以传入的整数列表结束):

public void getpath(抽象树、StringBuilder父路径、,
(列表)
StringBuilder newPath=parentPath.clone();
newPath.append(“/”);
append(tree.getNodeName());
add(newPath.toString().hashCode());
如果(!tree.isLeaf()){
Iterator child=tree.getChildren().Iterator();
for(AbstractTree子级:tree.getChildren()){
获取路径(子路径、新路径、列表)
}
}  
}
这仍然是O(n^2)。这是因为对值为O(n^2)的字符串(每个节点的路径长度与其深度成比例)进行散列,如果给定节点的散列只取其父节点路径的散列,并以某种方式对其进行修改,则可以将其降到O(n)

进一步的优化包括: -并行树遍历
-使用更智能的散列(即子路径的散列是子路径和父路径散列的函数,而不是整个父路径)。

我认为复杂性仍然相同。无论您是使用内联创建散列(O(n^2))还是在递归(O(n^2+n)=O(n^2))之后进行创建。
找到快速方法的唯一机会是在另一个地方做一些工作。e、 g.您可以在插入节点时对路径进行散列,只在另一点收集所有散列。

我不知道如何使用jvisualvm,但我已经使用100MB的XML树对这些方法进行了计时。创建路径…完成[3614ms]创建哈希代码…完成[962ms]总计完成[4576ms]在这种情况下,它不会确定核心问题,但您确实应该学习如何使用visualvm等探查器。这是解决性能问题的唯一专业方法。我强烈建议您学习如何使用分析器。最容易解决的问题是jvisualvm。@Michael,探查器会指出没有单一瓶颈,因此最有可能的是算法:)在树遍历期间试图计算哈希,但给出了错误的答案-也许你能理解为什么?(代码见原始问题)我改进了解决方案。现在应该更好了。我对这个解决方案有点困惑。首先,你是如何得到结果的?将列表作为参数传递会生成列表的副本,而不会修改原始列表。第二,克隆方法对parentPath不可见。Robert,传入列表不会被复制。它是通过引用传入的,因此您可以自由修改它。您的解决方案中也存在同样的问题。这听起来是一个有趣的解决方案-您有这样一个方法的示例吗?我没有时间提供工作代码,但基本上不是在StringBuilder实例中构建路径,而是将路径表示为路径元素的列表,每个元素都有一个名称和该元素的部分哈希。
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)
     }
  }  
}