我试图理解维基百科上的规范DFS伪代码(http://en.wikipedia.org/wiki/Depth-first_search) - 特别是使用堆栈的非递归实现。
在BFS中,您可以在将节点推送到队列之前检查节点是否已经被探索,这可以保证不会将任何节点多次推送到队列中。但是在DFS中,只检查当一个节点从堆栈中弹出时是否已经探索过它。这似乎是刻意的,因为维基百科页面说:“[DFS]延迟检查是否已经发现顶点,直到顶点从堆栈中弹出,而不是在推顶点之前进行此检查。”
但似乎这种延迟可能导致节点不止一次被推入堆栈。例如,考虑节点1指向节点2的图表,节点2指向节点3,指向节点4,依此类推,直至节点100.这些节点中的每一个都指向节点0.
考虑将Wikipedia DFS算法应用于此图,将节点1作为开始状态。首先,节点0将被推入堆栈。然后将推送节点2。接下来,节点2将从堆栈中弹出,并且由于它尚未被探索,它的子节点将被推入堆栈,(不检查它们是否已经被添加到堆栈中!)。节点2的子节点是节点0和节点3.接下来,节点3将被扩展,将节点0和节点4推入堆栈。这将持续到堆栈充满100次节点0。
我的问题是:为什么DFS与BFS有所不同,推迟检查是否会产生这种冗余?
答案 0 :(得分:1)
你是对的,在维基百科的非递归DFS实现中,每个Vertex将被放入Stack中的次数与入局边数一样多。
但我认为这不是出于意图。您可以看到Cormen等人的“数据结构和算法简介”中的递归实现。本书可以被认为比非递归DFS伪代码的源更“规范”。你可以看到那里的目标顶点在深入递归之前已经被检查过了。这似乎更合理。
因此,总而言之,我认为维基百科的实施存在非关键缺陷。一般来说,不要盲目相信“经典”是好方法。
答案 1 :(得分:0)
我的不好,我认为你在最后一句话中提出的问题一般是关于DFS ......在这个特定的实现中,推送到堆栈确实是多余的,可以通过添加if not visited
条件轻松避免在推送之前#39;
尽管如此,它并没有改变摊销的复杂性O(|E|+|V|)
答案 2 :(得分:0)
我认为你已经采用维基百科算法,而不是考虑访问/发现节点意味着什么。在BFS中,您可以将节点的所有子节点/相邻节点推入队列,但是您必须丢弃已经发现的节点; DFS的情况也是如此。
在BFS和DFS中,您可以选择:
BFS和DFS之间的唯一区别是,你正在推进BFS中的堆栈与DFS中的队列。