缓慢的建筑路径列表

时间:2009-07-24 11:30:07

标签: java optimization hash recursion tree

我正在构建一个哈希列表,表示树中的根节点路径。我的功能有效,但它们在大型树形结构上的速度非常慢 - 有更好的方法吗?我尝试在一个函数中构建列表,但是我得到了我不想要它们的独特哈希值。

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

4 个答案:

答案 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))。 找到快捷方式的唯一机会是在另一个地方做一些工作。例如您可以在插入节点时对路径进行散列,并仅在另一个点收集所有散列。