广度优先搜索算法(BFS)的原理
Posted loriby
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了广度优先搜索算法(BFS)的原理相关的知识,希望对你有一定的参考价值。
广度优先搜索(Breadth-First Search,简称BFS)是一种基础的图搜索算法,可以用于解决很多图论问题,如最短路径、连通性问题等。它的思想是从起始点开始,依次访问其所有直接相邻的节点,然后再依次访问这些相邻节点的相邻节点,直到找到目标节点或者搜索完整个图。
BFS一般采用队列来实现,从起始节点开始将其放入队列中,然后循环从队列中取出一个节点,遍历该节点的所有相邻节点,将未访问的相邻节点加入队列中。这样,每次取出的节点都是按照距离从起点越来越远的顺序进行访问,直到找到目标节点或者队列为空。
BFS算法的时间复杂度为O(N+M),其中N为图中的节点数,M为图中的边数。因为BFS遍历了所有的边和节点,所以它的时间复杂度与图的规模有关。在实际应用中,BFS的空间复杂度也比较高,因为需要保存已经访问过的节点和队列中的节点。
下面是一个基于BFS算法求解最短路径的示例:
假设有一张无向图如下所示:
1
/ \\
2 3
/ \\ / \\
4 5 6 7
图中有7个节点,从1到7的最短路径为1->3->7,路径长度为2。下面是BFS算法求解最短路径的Python代码实现:
from collections import deque
def bfs(graph, start, end):
queue = deque() # 创建队列
queue.append(start) # 将起始节点放入队列中
visited = set() # 创建已访问集合
visited.add(start) # 将起始节点标记为已访问
distance = start: 0 # 创建距离字典,记录每个节点到起始节点的距离
while queue: # 当队列非空时
node = queue.popleft() # 取出队列中的第一个节点
if node == end: # 如果找到了目标节点
return distance[end] # 返回起始节点到目标节点的距离
for neighbor in graph[node]: # 遍历当前节点的所有相邻节点
if neighbor not in visited: # 如果相邻节点未被访问过
visited.add(neighbor) # 标记相邻节点为已访问
distance[neighbor] = distance[node] + 1 # 计算相邻节点到起始节点的距离
queue.append(neighbor) # 将相邻节点加入队列中
from collections import deque
我们首先导入了Python标准库中的deque模块,用于创建双端队列。由于BFS算法需要使用队列来保存待访问节点,所以我们选择了使用双端队列来实现队列的功能。
def bfs(graph, start, end):
接下来定义了一个名为bfs的函数,该函数接受三个参数:graph表示待搜索的图,start表示搜索的起始节点,end表示搜索的目标节点。在函数体内我们将会使用这些参数来实现BFS算法。
queue = deque()
queue.append(start)
visited = set()
visited.add(start)
distance = start: 0
在函数体内,我们首先创建了一个空的双端队列queue,并将起始节点start加入队列中。同时,我们创建了一个空的集合visited,用于保存已经访问过的节点。因为起始节点是第一个被访问的节点,所以我们将其标记为已访问。
另外,我们创建了一个字典distance,用于保存每个节点到起始节点的距离。由于起始节点到自身的距离为0,所以我们将distance[start]的值设为0。
while queue:
node = queue.popleft()
if node == end:
return distance[end]
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
distance[neighbor] = distance[node] + 1
queue.append(neighbor)
在BFS算法的主循环中,我们首先使用while循环检查队列queue是否为空。如果队列为空,说明已经遍历完了整个连通图,BFS算法结束。
接下来,我们从队列的左侧(即队头)取出一个节点node,如果该节点就是我们要找的目标节点end,那么我们就返回distance[end],表示起始节点到目标节点的距离。
如果node不是目标节点,那么我们遍历它的所有相邻节点neighbor。如果neighbor未被访问过,我们将其标记为已访问,并计算它到起始节点的距离distance[neighbor]。由于neighbor是从node扩展出来的,所以它到起始节点的距离应该为distance[node]+1。最后,我们将neighbor加入队列queue中,以便在之后的搜索中访问它的相邻节点。
这样,每次从队列中取出的节点都是按照距离从起点越来越远的顺序进行访问,直到找到目标节点或者队列为空。当整个连通图都被遍历完后,我们就可以得到起始节点到目标节点的最短距离。
例如,我们可以使用以下的图来测试上述的BFS算法:
graph =
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
该图包含6个节点A、B、C、D、E、F和7条边,其中每个节点的相邻节点都以列表的形式保存在字典graph中。我们可以使用上述的bfs函数来计算起始节点A到目标节点F的最短距离:
>>> bfs(graph, 'A', 'F')
2
可以发现,起始节点A到目标节点F的最短距离为2,符合我们预期的结果。
在实际应用中,BFS算法经常被用于计算起点到其他节点的最短路径、网络分析、迷宫问题等等。掌握BFS算法可以帮助我们更好地理解图论和图算法,并解决一些实际问题。
基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)
参考技术A 深度优先搜索和广度优先搜索,都是图形搜索算法,它两相似,又却不同,在应用上也被用到不同的地方。这里拿一起讨论,方便比较。一、深度优先搜索
深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为DFS即Depth First Search。深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。一般用堆数据结构来辅助实现DFS算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。
基本步奏
(1)对于下面的树而言,DFS方法首先从根节点1开始,其搜索节点顺序是1,2,3,4,5,6,7,8(假定左分枝和右分枝中优先选择左分枝)。
(2)从stack中访问栈顶的点;
(3)找出与此点邻接的且尚未遍历的点,进行标记,然后放入stack中,依次进行;
(4)如果此点没有尚未遍历的邻接点,则将此点从stack中弹出,再按照(3)依次进行;
(5)直到遍历完整个树,stack里的元素都将弹出,最后栈为空,DFS遍历完成。
二、广度优先搜索
广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历算法这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。基本过程,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。一般用队列数据结构来辅助实现BFS算法。
基本步奏
(1)给出一连通图,如图,初始化全是白色(未访问);
(2)搜索起点V1(灰色);
(3)已搜索V1(黑色),即将搜索V2,V3,V4(标灰);
(4)对V2,V3,V4重复以上操作;
(5)直到终点V7被染灰,终止;
(6)最短路径为V1,V4,V7.
以上是关于广度优先搜索算法(BFS)的原理的主要内容,如果未能解决你的问题,请参考以下文章