【发布时间】:2013-12-24 02:58:09
【问题描述】:
在我正在学习的算法课程中,据说深度优先搜索 (DFS) 比广度优先搜索 (BFS) 更节省空间。
这是为什么呢?
虽然它们基本上做同样的事情,但在 DFS 中,我们堆叠当前节点的后继节点,而在 BFS 中,我们将后继节点加入队列。
【问题讨论】:
标签: algorithm graph-algorithm breadth-first-search depth-first-search
在我正在学习的算法课程中,据说深度优先搜索 (DFS) 比广度优先搜索 (BFS) 更节省空间。
这是为什么呢?
虽然它们基本上做同样的事情,但在 DFS 中,我们堆叠当前节点的后继节点,而在 BFS 中,我们将后继节点加入队列。
【问题讨论】:
标签: algorithm graph-algorithm breadth-first-search depth-first-search
您的困惑源于您显然假设可以通过将 FIFO 队列替换为 LIFO 堆栈从 BFS 算法中获得 DFS 算法。
这是一个普遍的误解——它根本不是真的。经典的 DFS 算法无法通过将 BFS 队列替换为堆栈来获得。这些算法之间的差异更为显着。
如果您采用 BFS 算法并简单地将 FIFO 队列替换为 LIFO 堆栈,您将获得可以称为 pseudo-DFS 算法的东西。这种伪 DFS 算法确实会正确复现 DFS 顶点前向遍历序列,但不会有 DFS 空间效率,也不支持 DFS 后向遍历(回溯)。
同时,通过这种幼稚的队列到堆栈替换,无法从 BFS 获得真正经典的 DFS。经典的 DFS 是一种完全不同的算法,具有明显不同的核心结构。真正的 DFS 是一种真正的递归 算法,它使用堆栈回溯 目的,而不是用于存储顶点发现“前端”(如 BFS 中的情况)。最直接的结果是,在 DFS 算法中,最大堆栈深度等于在 DFS 遍历中与原点顶点的最大距离。在 BFS 算法中(如在前面提到的伪 DFS 中),最大队列大小等于最大顶点发现前沿的宽度。
说明 DFS 和 BFS(以及伪 DFS)之间的峰值内存消耗差异的最突出和极端的示例是星图:单个中心顶点被大量数字包围(例如,1000 ) 的外围顶点,每个外围顶点通过边连接到中心顶点。如果你在这个图上使用中心顶点作为原点运行 BFS,队列大小将立即跳转到1000。如果您使用伪 DFS(即,如果您只是用堆栈替换队列),同样的事情显然会发生。但是经典的 DFS 算法只需要 1 (!) 的堆栈深度来遍历整个图。看到不同? 1000 与 1。这就是 DFS 更好的空间效率的意思。
基本上,找一本关于算法的书,找到对经典 DFS 的描述,看看它是如何工作的。您会注意到,BFS 和 DFS 之间的区别远比单纯的队列与堆栈的区别要广泛得多。
附:还应该说,可以构建一个在 BFS 下峰值内存消耗较小的图示例。因此,关于 DFS 更好的空间效率的陈述应该被视为可能“平均”适用于某些隐含的“漂亮”图类的东西。
【讨论】:
O(depth * branching factor) 空间,而不是 O(depth) 以获得正确的 DFS,这仍然比 O(width) 更好。似乎您的回答说了类似的话,尽管我认为这更简洁明了。
在 DFS 中,您只需要与完全平衡树上的深度 O(log(n)) 线性相关的空间,而 BFS(广度优先搜索)需要 O(n)(树的最宽部分是二叉树中的最低深度树 n/2 个节点)。
例子:
1
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
2 2
/ \ / \
/ \ / \
/ \ / \
/ \ / \
3 3 3 3
/ \ / \ / \ / \
/ \ / \ / \ / \
4 4 4 4 4 4 4 4
/ \ / \ / \ / \ / \ / \ / \ / \
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
DFS 需要空间:4
BFS 需要在倒数第二行空间 8
如果分支因子更高,情况会变得更糟
【讨论】:
O-Notation O(n/2) = O(n/4) = O(n) 中没有区别。