【发布时间】:2014-04-04 01:07:40
【问题描述】:
问题一:
对于有根树,比如二叉树,为什么人们要确保永远不会将 NULL/nil/null 压入堆栈?
例如
while(!stack.empty() ) {
x = stack.pop();
visit(x);
if(x->right) stack.push(x->right);
if(x->left) stack.push(x->left);
}
而不是
while(!stack.empty() ) {
x = stack.pop();
if(!x) continue;
visit(x);
stack.push(x->left), stack.push(x->right);
}
我的意思是,第二种形式更自然地与 preorder/DFS 的递归形式对齐,那么为什么人们通常将“check before”与 iterative 一起使用,而“check after”与 recursive 一起使用?除了节省(n + 1)个堆栈点(这不会增加空间复杂度)之外,我还有什么遗漏的原因吗?
问题2:
图上的DFS:使用迭代,为什么人们在推送当前节点的相邻节点时设置visited标志,但在递归中,我们只有在递归发生后才这样做?
例如迭代:
while(!stack.empty()){
x=stack.pop();
// we do not need to check if x is visited after popping
for all u adjacent from x
if(!visited[u]) stack.push(u), visited[u]=true; //we check/set before push
}
但在递归中:
void DFS(Graph *G, int x)
{
if( !visited[x] ) return; //we check/set after popping into node
visited[x]=true;
for all u adjacent from x
DFS(G, u); //we do not check if u is already visited before push
}
所以一般来说,两个问题之间的联系是:为什么我们在将有效的东西推送到实际的堆栈对象之前更加小心(DFS 的迭代方法),但在使用硬件堆栈时却不那么小心(递归)?我错过了什么吗?硬件堆栈不是更“奢侈”吗,因为它会溢出(对于堆栈对象,我们可以将它们声明为堆外)?
感谢好心人的见解。
[ 这不仅仅是简单的编码风格,而是对算法编码方式的全面重新安排。我说的是对硬件堆栈的粗心与对软件堆栈的小心?我还想知道一些技术差异(即,它们在所有情况下是否真的是平等的方法)。几乎所有的书都遵循上述模式。 ]
【问题讨论】:
-
你的问题只是关于代码风格。这完全是口味问题。我不认为这个问题是这里的主题。
-
我也怀疑您声称的观察结果是否普遍正确
标签: algorithm recursion stack depth-first-search iteration