我有一个n-ary树,其中包含每个节点中的键值(整数)。我想计算树的最小深度。这是我到目前为止所提出的:
int min = 0;
private int getMinDepth(Node node, int counter, int temp){
if(node == null){
//if it is the first branch record min
//otherwise compare min to this value
//and record the minimum value
if(counter == 0){
temp = min;
}else{
temp = Math.min(temp, min);
min = 0;
}
counter++;//counter should increment by 1 when at end of branch
return temp;
}
min++;
getMinDepth(node.q1, counter, min);
getMinDepth(node.q2, counter, min);
getMinDepth(node.q3, counter, min);
getMinDepth(node.q4, counter, min);
return temp;
}
代码的调用如下:
int minDepth = getMinDepth(root, 0, 0);
这个想法是,如果树正在遍历第一个分支(分支号由计数器跟踪),那么我们设置临时持有者来存储该分支深度。从那时起,比较下一个分支长度,如果它更小,则使temp =该长度。由于某种原因,计数器根本没有递增并始终保持为零。谁知道我做错了什么?
答案 0 :(得分:2)
我认为你最好先进行广度优先搜索。您当前的实现尝试深度优先,这意味着如果分支恰好处于尴尬的顺序,它最终可能会探索整个树。
要进行广度优先搜索,您需要一个队列(ArrayDeque可能是正确的选择)。然后,您需要一个包含节点和深度的小类。算法有点像这样:
Queue<NodeWithDepth> q = new ArrayDeque<NodeWithDepth>();
q.add(new NodeWithDepth(root, 1));
while (true) {
NodeWithDepth nwd = q.remove();
if (hasNoChildren(nwd.node())) return nwd.depth();
if (nwd.node().q1 != null) q.add(new NodeWithDepth(nwd.node().q1, nwd.depth() + 1));
if (nwd.node().q2 != null) q.add(new NodeWithDepth(nwd.node().q2, nwd.depth() + 1));
if (nwd.node().q3 != null) q.add(new NodeWithDepth(nwd.node().q3, nwd.depth() + 1));
if (nwd.node().q4 != null) q.add(new NodeWithDepth(nwd.node().q4, nwd.depth() + 1));
}
这看起来它比深度优先搜索使用更多内存,但是当你认为堆栈帧消耗内存时,这将探索比深度优先搜索更少的树,你会发现它不是案件。可能。
无论如何,看看你如何继续下去。
答案 1 :(得分:2)
您是按值传递计数器变量,而不是按引用传递。因此,对它做出的任何更改都是当前堆栈帧的本地更改,并且一旦函数返回并且该帧被弹出堆栈就会丢失。 Java不支持通过引用传递基元(或任何真正的基元),因此您必须将其作为单个元素数组传递或将其包装在对象中以获取您正在寻找的行为。
这是一个更简单(未经测试)的版本,无需通过引用传递变量:
private int getMinDepth(QuadTreeNode node){
if(node == null)
return 0;
return 1 + Math.min(
Math.min(getMinDepth(node.q1), getMinDepth(node.q2)),
Math.min(getMinDepth(node.q3), getMinDepth(node.q4)));
}
你的版本和上面的版本都是低效的,因为它们搜索整个树,而实际上你只需要搜索到最浅的深度。要有效地执行此操作,请使用队列进行像Tom推荐的广度优先搜索。但请注意,获得额外速度所需的权衡是队列使用的额外内存。
编辑:
我决定继续编写一个广度优先的搜索版本,它不会假设你有一个跟踪节点深度的类(比如Tom的NodeWithDepth)。再一次,我还没有对它进行过测试,甚至没有对它进行过编译......但我认为它应该足以让你继续前进,即使它没有开箱即用。此版本应该在大型复杂树上执行得更快,但也会使用更多内存来存储队列。
private int getMinDepth(QuadTreeNode node){
// Handle the empty tree case
if(node == null)
return 0;
// Perform a breadth first search for the shallowest null child
// while keeping track of how deep into the tree we are.
LinkedList<QuadTreeNode> queue = new LinkedList<QuadTreeNode>();
queue.addLast(node);
int currentCountTilNextDepth = 1;
int nextCountTilNextDepth = 0;
int depth = 1;
while(!queue.isEmpty()){
// Check if we're transitioning to the next depth
if(currentCountTilNextDepth <= 0){
currentCountTilNextDepth = nextCountTilNextDepth;
nextCountTilNextDepth = 0;
depth++;
}
// If this node has a null child, we're done
QuadTreeNode node = queue.removeFirst();
if(node.q1 == null || node.q2 == null || node.q3 == null || node.q4 == null)
break;
// If it didn't have a null child, add all the children to the queue
queue.addLast(node.q1);
queue.addLast(node.q2);
queue.addLast(node.q3);
queue.addLast(node.q4);
// Housekeeping to keep track of when we need to increment our depth
nextCountTilNextDepth += 4;
currentCountTilNextDepth--;
}
// Return the depth of the shallowest node that had a null child
return depth;
}
答案 2 :(得分:0)
计数器总是保持为零,因为java中的原语是按值调用的。这意味着如果您覆盖函数调用中的值,调用者将看不到更改。或者,如果您熟悉C ++表示法,则它是foo(int x)而不是foo(int&amp; x)。
一种解决方案是使用Integer对象,因为对象是按引用调用的。
由于您对最小深度感兴趣,广度优先解决方案可以正常工作,但您可能会遇到大树的内存问题。
如果您认为树可能变得相当大,那么IDS解决方案将是最好的。这样,您将获得广度第一变体的时间复杂度以及深度优先解决方案的空间复杂性。
这是一个小例子,因为IDS并不像它的兄弟那么出名(虽然对于严肃的东西更有用!)。为了简单起见,我假设每个节点都有一个包含子节点的列表(因为它更通用)。
public static<T> int getMinDepth(Node<T> root) {
int depth = 0;
while (!getMinDepth(root, depth)) depth++;
return depth;
}
private static<T> boolean getMinDepth(Node<T> node, int depth) {
if (depth == 0)
return node.children.isEmpty();
for (Node<T> child : node.children)
if (getMinDepth(child, depth - 1)) return true;
return false;
}
有关简短说明,请参阅http://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search