对于有根树,比如二叉树,人们为什么要确保永远不要将NULL / nil / null压入堆栈?
e.g。
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);
}
我的意思是,第二种形式更自然地与预订/ DFS的递归形式对齐,那么为什么人们通常使用迭代的“检查前”,并且使用递归“检查”?除了保存(n + 1)堆栈点(不会增加空间复杂度)之外,我缺少任何理由?
DFS on Graph:使用迭代,为什么人们在推送当前节点的相邻节点时设置了访问标志,但是在递归时,我们只在递归发生后才这样做?
e.g。迭代:
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的迭代方法)之前我们会更加小心,但在使用硬件堆栈时(递归)则不那么小心?我错过了什么吗?硬件堆栈不是更“奢侈”,因为它可以溢出(对于堆栈对象,我们可以将它们从堆中声明)?
感谢有识之士的见解。
[这不仅仅是编码风格,而是关于算法编码方式的总重排。我说的是对硬件堆栈的粗心大意而不是小心软件堆栈?我也想知道一些技术差异(即,它们对于所有情况都是真正平等的方法)。几乎所有书都遵循上述模式。 ]
答案 0 :(得分:1)
检查推送时间或弹出时间均正常,但检查推送效果何时更好,
例如,如果你有一个深度为10的二叉树,如果你检查弹出的时间,你基本上就是遍历一棵深度为11的树(就像你为每一片叶子添加了两个以上的更多的孩子),那就是浪费了2048多个操作。如果它是递归的,则意味着它将使2048更多的必要函数调用。
除了可以在之前或之后检查之外,您看到的代码就是这样写的。