深度优先搜索是否会创建冗余?

时间:2014-09-23 07:06:49

标签: algorithm depth-first-search breadth-first-search

我试图理解维基百科上的规范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有所不同,推迟检查是否会产生这种冗余?

3 个答案:

答案 0 :(得分:1)

你是对的,在维基百科的非递归DFS实现中,每个Vertex将被放入Stack中的次数与入局边数一样多。

但我认为这不是出于意图。您可以看到Cormen等人的“数据结构和算法简介”中的递归实现。本书可以被认为比非递归DFS伪代码的源更“规范”。你可以看到那里的目标顶点在深入递归之前已经被检查过了。这似乎更合理。

因此,总而言之,我认为维基百科的实施存在非关键缺陷。一般来说,不要盲目相信“经典”是好方法。

答案 1 :(得分:0)

我的不好,我认为你在最后一句话中提出的问题一般是关于DFS ......在这个特定的实现中,推送到堆栈确实是多余的,可以通过添加if not visited条件轻松避免在推送之前#39;

尽管如此,它并没有改变摊销的复杂性O(|E|+|V|)

答案 2 :(得分:0)

我认为你已经采用维基百科算法,而不是考虑访问/发现节点意味着什么。在BFS中,您可以将节点的所有子节点/相邻节点推入队列,但是您必须丢弃已经发现的节点; DFS的情况也是如此。

在BFS和DFS中,您可以选择:

  1. 不推送已发现的节点。 OR
  2. 推送所有相邻节点,但在弹出时丢弃那些已经发现的节点。
  3. BFS和DFS之间的唯一区别是,你正在推进BFS中的堆栈与DFS中的队列。