我有一个单向的对象树,其中每个对象都指向其父对象。给定一个对象,我需要获取其整个子孙子树,作为对象的集合。对象实际上并不在任何数据结构中,但我可以轻松获得所有对象的集合。
天真的方法是检查批处理中的每个对象,查看给定对象是否是祖先,并将其保留在一边。这样效率不会太高......它带有O(N * N)的开销,其中N是对象的数量。
另一种方法是递归方法,即搜索对象的直接子节点并重复下一级别的过程。不幸的是,这棵树是单向的......对孩子们来说没有直接的方法,这只会比以前的方法成本稍低。
我的问题:我在这里忽略了一种有效的算法吗?
谢谢,
Yuval = 8 - )
答案 0 :(得分:3)
数据库的工作方式相同,数据库也是如此。构建一个从父级映射到子级列表的哈希表。这需要O(n)。然后使用该哈希表可以使查找和查询更有效率。
答案 1 :(得分:3)
正如其他人所提到的,将对象的哈希表/地图构建到他们(直接)孩子的列表中。
从那里,您可以轻松查找“目标对象”的直接子项列表,然后对列表中的每个对象重复此过程。
以下是我在Java中使用泛型的方式,使用队列而不是任何递归:
public static Set<Node> findDescendants(List<Node> allNodes, Node thisNode) {
// keep a map of Nodes to a List of that Node's direct children
Map<Node, List<Node>> map = new HashMap<Node, List<Node>>();
// populate the map - this is O(n) since we examine each and every node
// in the list
for (Node n : allNodes) {
Node parent = n.getParent();
if (parent != null) {
List<Node> children = map.get(parent);
if (children == null) {
// instantiate list
children = new ArrayList<Node>();
map.put(parent, children);
}
children.add(n);
}
}
// now, create a collection of thisNode's children (of all levels)
Set<Node> allChildren = new HashSet<Node>();
// keep a "queue" of nodes to look at
List<Node> nodesToExamine = new ArrayList<Node>();
nodesToExamine.add(thisNode);
while (nodesToExamine.isEmpty() == false) {
// pop a node off the queue
Node node = nodesToExamine.remove(0);
List<Node> children = map.get(node);
if (children != null) {
for (Node c : children) {
allChildren.add(c);
nodesToExamine.add(c);
}
}
}
return allChildren;
}
如果我记得如何计算这个权利,那么预期的执行时间就在O(n)和O(2n)之间。您可以保证查看列表中的每个节点,再加上一些操作来查找节点的所有后代 - 在最坏的情况下(如果在根节点上运行算法),您正在查看每个节点中的每个节点列表两次。
答案 2 :(得分:0)
您的问题有点抽象,但nested sets(向下滚动,可能有点过于特定于mysql)可能是您的选择。读取操作速度非常快,但任何修改都非常复杂(并且必须平均修改一半树)。
但是,这需要能够修改您的数据结构。我想如果你可以修改结构,你也可以添加对子对象的引用。如果你不能修改结构,我怀疑有什么比你的想法更快。
答案 3 :(得分:0)
构建一个树,其中的对象指向他们的直接孩子可能是最好的方法,特别是如果你需要做未来的查找。构建树在很大程度上取决于原始树的高度。最多需要O(n ^ 2)。
在构建树时,构建一个哈希表。哈希表将使未来更快地搜索特定对象(O(1)与O(n))。