根据维基百科的说法,DFS和BFS的实施基本上有两个不同。
他们是:
1)DFS使用堆栈,而BFS使用队列。(我理解)。
2)DFS延迟检查是否已发现顶点,直到从堆栈弹出顶点,而不是在推顶点之前进行此检查。
我无法理解第二个区别。我的意思是为什么DFS在从堆栈中删除后访问节点,而BFS在将节点添加到队列之前访问该节点。
谢谢!
额外信息:
在上面两个算法的一个简单实现中,我们采用一个布尔数组(让我们命名它访问)来跟踪访问哪个节点。问题提到了这个访问过的布尔数组。
答案 0 :(得分:0)
wikipedia article提到了两种执行DFS的方法:使用递归和使用堆栈。
为了完整起见,我在这里复制:
使用递归
procedure DFS(G,v):
label v as discovered
for all edges from v to w in G.adjacentEdges(v) do
if vertex w is not labeled as discovered then
recursively call DFS(G,w)
使用堆栈
procedure DFS-iterative(G,v):
let S be a stack
S.push(v)
while S is not empty
v ← S.pop()
if v is not labeled as discovered:
label v as discovered
for all edges from v to w in G.adjacentEdges(v) do
S.push(w)
这里要知道的重要一点是方法调用是如何工作的。有一个底层堆栈,让我们调用T
。在调用方法之前,它的参数被推送到堆栈上。然后,该方法再次从该堆栈中获取参数,执行其操作,并将其结果推回堆栈。然后通过调用方法从堆栈中获取该结果。
例如,请考虑以下代码段:
function caller() {
callResult = callee(argument1, argument2);
}
就堆栈T
而言,这就是(示意性地):
// inside method caller
T.push(argument1);
T.push(argument2);
"call method callee"
// inside method callee
argument2 = T.pop();
argument1 = T.pop();
"do something"
T.push(result);
"return from method callee"
// inside method caller
callResult = T.pop();
这几乎是第二个实现中发生的事情:显式使用堆栈。您可以比较在第一个片段中调用DFS
并在堆栈上推送顶点,并比较在第一个片段中执行对DFS
的调用,并从堆栈中弹出顶点。
从堆栈中弹出顶点v
后的第一件事就是将其标记为已发现。这相当于将其标记为执行DFS
时的第一步。
答案 1 :(得分:0)
这可能是我第一次听说DFS会延迟设置"发现"属性直到从堆栈中弹出(即使在wikipedia上,递归和迭代伪代码都将当前节点标记为在推送堆栈中的子节点之前发现的)。此外,如果您发现"只有当你完成处理它时,我认为你可以很容易地进入无限循环。
然而,有些情况我为每个节点使用两个标志:一个在进入时设置,一个在离开节点时(通常,我将DFS写为递归,因此,在递归函数的末尾)。我认为我使用的东西就是我需要的东西:强连接组件或连接图中的关键点(="节点,如果删除,图表将失去连接")。此外,退出节点的顺序通常用于topological sorting(拓扑排序只是处理节点的顺序的倒数)。