什么时候使用深度优先搜索(DFS)和广度优先搜索(BFS)?[关闭]


345

我了解DFS和BFS之间的区别,但是我想知道什么时候使用另一种比较实用?

任何人都可以举例说明DFS将如何胜过BFS,反之亦然?


4
也许您可以在问题中提到DFS和BFS的完整术语-人们可能不知道这些缩写。
汉斯·彼得·斯托尔




注意提到了BFS和DFS的一些应用场景
Yossarian42

Answers:


353

这在很大程度上取决于搜索树的结构以及解决方案(又名搜索项目)的数量和位置。

  • 如果您知道解决方案离树的根并不远,那么广度优先搜索(BFS)可能更好。
  • 如果树很深并且解决方案很少,则深度优先搜索(DFS)可能会花费很长时间,但BFS可能会更快。

  • 如果树很宽,则BFS可能需要太多内存,因此可能是完全不切实际的。

  • 如果解决方案很常见但位于树的深处,则BFS可能不切实际。

  • 如果搜索树非常深,则无论如何都需要限制深度优先搜索(DFS)的搜索深度(例如,使用迭代加深)。

但是,这些只是经验法则。您可能需要进行实验。


4
AFAIK递归通常需要比迭代更多的内存。
Marek Marczak

3
@MarekMarczak我不太明白你想说什么。如果将BFS作为迭代-如果解决方案空间不易枚举,则可能必须将搜索树的整个第n级存储在内存中以枚举第n + 1级。
汉斯·彼得·斯特尔

11
@MarekMarczak DFS的迭代版本使用堆栈。递归与迭代是一个完整的主题。
克林特·德古

想到的另一种情况是:BFS在图形为“无限”的情况下很有用(必要)。就像说,一个棋盘向各个方向延伸到无穷大。DFS将永远不会返回。如果条件可以满足,BFS可以保证找到要搜索的内容。
ThePartyTurtle

157

深度优先搜索

深度优先搜索通常用于模拟游戏(以及现实世界中类似游戏的情况)。在典型的游戏中,您可以选择几种可能的动作之一。每个选择都会导致进一步的选择,每个选择都会导致进一步的选择,依此类推成为不断扩大的可能性树形图。

在此处输入图片说明

例如,在象棋,井字游戏这样的游戏中,当您决定要做什么时,您可以在脑海中想象一个动作,然后是对手的可能反应,然后是反应,等等。您可以通过查看哪个步骤可以带来最佳结果来决定要做什么。

游戏树中只有一些路径才能赢得胜利。有些导致您的对手获胜,当您达到这样的结局时,您必须备份或回溯到上一个节点,然后尝试其他路径。通过这种方式,您可以探索树,直到找到成功结论的路径。然后,您将沿着此路径迈出第一步。


广度优先搜索

广度优先搜索具有一个有趣的属性:它首先查找距起点一个边缘的所有顶点,然后查找相距两个边缘的所有顶点,依此类推。如果您要查找从起始顶点到给定顶点的最短路径,这将很有用。启动BFS,然后找到指定的顶点,便知道到目前为止跟踪的路径是到该节点的最短路径。如果路径更短,BFS将会找到它。

广度优先搜索可用于在点对点网络(例如BitTorrent)中查找邻居节点,GPS系统用于查找附近位置,社交网站用于查找指定距离内的人等等。


113

来自http://www.programmerinterview.com/index.php/data-structures/dfs-vs-bfs/的不错的解释

BFS的一个例子

这是BFS外观的示例。这类似于“级别顺序树遍历”,在这里我们将使用“队列”和“迭代”方法(大多数“回溯”将以DFS结尾)。这些数字表示在BFS中访问节点的顺序:

在此处输入图片说明

在深度优先搜索中,您从树的根部开始,并尽可能沿树的一个分支移动,直到找到要查找的节点或击中叶节点(没有子节点的节点)为止。如果您命中了叶子节点,那么您将在最近的祖先中搜索未探索的孩子。

DFS的一个例子

这是DFS外观的示例。我认为二叉树中的后顺序遍历将首先从Leaf级别开始工作。这些数字表示在DFS中访问节点的顺序:

在此处输入图片说明

DFS和BFS之间的区别

比较BFS和DFS,DFS的最大优点是它的内存需求比BFS低得多,因为不必在每个级别存储所有子指针。根据数据和您要查找的内容,DFS或BFS可能都是有利的。

例如,给定一棵家谱,如果有人正在树上寻找一个还活着的人,那么可以安全地假设那个人在树的底部。这意味着BFS要花费很长时间才能达到最后一个级别。但是,DFS可以更快地找到目标。但是,如果要寻找一个很久以前就去世的家庭成员,那么这个人将离树顶很近。然后,BFS通常比DFS更快。因此,这两种方法的优势都取决于数据和所需的内容。

另一个例子是Facebook。对朋友之友的建议。我们需要直接的朋友提出建议,以便在哪里可以使用BFS。可能是找到最短路径或检测到循环(使用递归),我们才可以使用DFS。


什么是“二叉树中的后遍历”?
凯尔·德莱尼

8
DFS是否找到比BFS更好的最短路径?我认为这是另一回事。我认为BFS找到最短的路径。是不是
Naveen Gabriel

1
如果您将功劳归功于源,将不胜感激。比较部分摘自Narasimha Karumanchi的“用Java简化数据结构”。
Learntogrow-growtolearn

当然,我可以更新此信息,但不要指望有人在这里表示赞赏。只想帮助像我这样的可怜的技术人员。
卡纳加维卢·苏古玛

1
@KyleDelaney您可以通过三个顺序遍历一棵树-预顺序,顺序和后顺序。它们分别对应于前缀infix和postfix表示法。当您遍历树然后再备份时,如果您是第一次订购该节点,则是预定的;如果是第二次订购,则是最后一次订购。实际上,您可以通过这种方式序列化该树,并且只要您记住所使用的顺序,就可以从序列化中重建该树。
戴夫

43

当树的深度可以变化时,广度优先搜索通常是最好的方法,而您只需要搜索树的一部分即可找到解决方案。例如,找到从起始值到最终值的最短路径是使用BFS的好地方。

当您需要搜索整棵树时,通常使用深度优先搜索。与BFS相比,它更易于实现(使用递归),并且所需的状态更少:BFS需要存储整个“边界”,而DFS仅需要存储当前元素的父节点列表。


26

DFS比BFS更节省空间,但可能会达到不必要的深度。

他们的名字揭示了:如果广度很大(即分支系数很大),但是深度非常有限(例如“动作”的数量有限),那么DFS可能比BFS更可取。


在IDDFS上

应该提到的是,有一个鲜为人知的变种,它结合了DFS的空间效率,但是(累积地)BFS的层级访问是迭代的加深深度优先搜索。该算法会重访某些节点,但它只会贡献一个渐近差异的恒定因子。


1
它不一定更节省空间。例如考虑一个路径图。
RB

16

当您以程序员的身份处理此问题时,一个因素很明显:如果您使用的是递归,则深度优先搜索更易于实现,因为您不需要维护包含要探索的节点的其他数据结构。

如果要在节点中存储“已经访问过”的信息,这是对非定向图的深度优先搜索:

def dfs(origin):                               # DFS from origin:
    origin.visited = True                      # Mark the origin as visited
    for neighbor in origin.neighbors:          # Loop over the neighbors
        if not neighbor.visited: dfs(next)     # Visit each neighbor if not already visited

如果将“已访问”信息存储在单独的数据结构中:

def dfs(node, visited):                        # DFS from origin, with already-visited set:
    visited.add(node)                          # Mark the origin as visited
    for neighbor in node.neighbors:            # Loop over the neighbors
        if not neighbor in visited:            # If the neighbor hasn't been visited yet,
            dfs(node, visited)                 # then visit the neighbor
dfs(origin, set())

与广度优先搜索相反,无论哪种情况,您都需要为尚未访问的节点列表维护单独的数据结构。



5

对于BFS,我们可以考虑Facebook示例。我们收到建议从FB个人资料中添加其他其他朋友个人资料中的朋友。假设A-> B,而B-> E和B-> F,那么A会得到E和F的建议。他们必须使用BFS读取直到第二级。DFS更基于我们要根据从源到目标的数据来预测某些内容的方案。如前所述,关于国际象棋或数独。一旦这里有所不同,我相信DFS应该用于最短路径,因为DFS首先会覆盖整个路径,然后我们才能决定最佳路径。但是由于BFS将使用贪婪的方法,因此看起来它是最短的路径,但最终结果可能会有所不同。让我知道我的理解是否错误。


现在我的评论有点晚了。但是要找到最短的路径,应使用BFS。但是,“ DFS更基于我们要根据从源头到目的地的数据来预测某物的方案”,这是您所说的一件绝妙的事情!荣誉!
Oskarzito

4

有些算法取决于DFS(或BFS)的特定属性才能起作用。例如,用于查找2连接组件的Hopcroft和Tarjan算法利用了以下事实:DFS遇到的每个已经访问的节点都在从根到当前探索的节点的路径上。


4

以下是您所要询问的全面答案。

简单来说:

广度优先搜索(BFS)算法从其名称“ Breadth”开始,通过节点的外边缘发现节点的所有邻居,然后通过其外边缘发现先前提到的邻居的未访问邻居,依此类推,直到所有可以访问原始来源可访问的节点(如果还有其他未访问的节点等,我们可以继续使用其他原始来源)。这就是为什么如果边缘的权重是均匀的,则可以使用它找到从一个节点(原始源)到另一个节点的最短路径(如果有)。

深度优先搜索(DFS)算法从其名称“深度”开始,通过其外边缘发现了最近发现的节点x的未访问邻居。如果没有来自节点x的未访问邻居,则该算法回溯以发现从中发现了节点x的节点的未访问邻居(通过其外部边缘),依此类推,直到访问了可从原始源到达的所有节点为止。 (如果还有其他未访问的节点等,我们可以继续并采用其他原始资源)。

BFS和DFS都可能不完整。例如,如果节点的分支因数是无限的,或者对于要支持的资源(内存)来说很大(例如,当存储下一个要发现的节点时),那么即使搜索的键距一定距离,BFS也不会完成原始来源的少数边缘。此无限分支因子可能是由于要从给定节点进行发现而进行的无限选择(相邻节点)。如果深度是无限的,或者对于要支持的资源(内存)来说很大(例如,当存储下一个要发现的节点时),则DFS不会完成,即使搜索到的键可以是原始数据源的第三个邻居。此无限深度可能是由于以下情况:对于算法发现的每个节点,至少存在一个以前未曾访问过的新选择(邻居节点)。

因此,我们可以得出何时使用BFS和DFS的结论。假设我们正在处理一个可管理的有限分支因子和一个可管理的有限深度。如果搜索到的节点较浅,即在从原始源经过某些边缘之后可以到达,则最好使用BFS。另一方面,如果搜索到的节点较深,即从原始源经过很多边缘后可以到达,则最好使用DFS。

例如,在社交网络中,如果我们要搜索与某个特定人具有相似兴趣的人,则可以将这个人的BFS用作原始来源,因为这些人大多是他的直接朋友或朋友的朋友,即一个人或两个边缘远。另一方面,如果我们要搜索与特定人的兴趣完全不同的人,则可以将此人的DFS用作原始来源,因为这些人大多与他相距很远,即朋友的朋友的朋友....即太多的边缘。

BFS和DFS的应用程序也可能会有所不同,这是因为每个搜索引擎的搜索机制不同。例如,当我们只想检查从一个节点到另一个节点的可达性而又不知道该节点的位置时,可以使用BFS(假设分支因子是可管理的)或DFS(假设深度是可管理的)。他们俩还可以解决相同的任务,例如图形的拓扑排序(如果有的话)。BFS可用于查找从一个节点(原始源)到另一个节点的最短路径(具有单位权重边缘)。鉴于DFS可以深入研究,因此可以用来穷尽所有选择,例如发现非循环图中两个节点之间的最长路径。DFS还可用于图形中的循环检测。

最后,如果我们具有无限的深度和无限的分支因子,则可以使用迭代加深搜索(IDS)。


2

根据DFS和BFS的属性。例如,当我们想找到最短的路径时。我们通常使用bfs,它可以保证“最短”。但是dfs只能保证我们可以从这一点来实现这一点,不能保证“最短”。


2

我认为这取决于您面临的问题。

  1. 简单图上的最短路径-> bfs
  2. 所有可能的结果-> dfs
  3. 在图上搜索(处理树,martix也作为图)-> dfs ....

如果您在列表之前添加空行,答案会更好。
montonero

1

由于深度优先搜索在处理节点时会使用堆栈,因此DFS提供了回溯功能。因为广度优先搜索使用队列而不是堆栈来跟踪要处理的节点,所以BFS不提供回溯。


1

当树的宽度很大并且深度很低时,请使用DFS,因为递归堆栈不会溢出。当树的宽度很小并且深度很大时请使用BFS遍历树。


0

这是一个很好的例子,可以证明BFS在某些情况下比DFS更好。https://leetcode.com/problems/01-matrix/

正确实施后,两种解决方案都应访问距离比当前单元+1更远的单元。但是DFS效率低下,并且反复访问同一单元,从而导致O(n * n)复杂性。

例如,

1,1,1,1,1,1,1,1, 
1,1,1,1,1,1,1,1, 
1,1,1,1,1,1,1,1, 
0,0,0,0,0,0,0,0,

0

这取决于使用的情况。每当遇到遍历图形的问题时,我们都会出于某种目的进行处理。当在未加权图中找到最短路径或发现图中是否为二分图时,我们可以使用BFS。对于周期检测或任何需要回溯的逻辑问题,我们可以使用DFS。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.