06-广度优先搜索:图、队列

数据结构和算法
基于《算法图解》—Aditya Bhargava 和《数据结构》—严蔚敏

第6章广度优先搜索

6.1 简介
广度优先搜索—breadth-first search,BFS.
主要内容图和队列。
广度优先搜索能让你能够找出两样东西之间的最短距离,比如:编写国际跳棋AI(Artificial Intelligence),计算最少走多少步可以获胜;或者根据你的人际关系网络找到关系最近的医生。

需要两个步骤:

(1)使用图来建立问题模型。
(2)使用广度优先搜索解决问题。

6.2 什么是图
图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。
在这里插入图片描述

6.3 广度优先搜索
广度优先搜索是一种用于图的查找算法,可帮助回答两类问题:

  • 第一类问题:从节点A出发,有前往节点B的路径吗?
  • 第二类问题:从节点A出发,前往节点B的哪条路径最短?

假设你经营着一个芒果农场,需要寻找芒果销售商,以便将芒果卖给他。为此,你可在朋友中查找。
在这里插入图片描述

首先创建一个朋友名单,然后依次检查名单中的每个人,是否是芒果销售商:
在这里插入图片描述

假设你没有朋友是芒果销售商,那么你就必须在朋友的朋友中查找。
在这里插入图片描述

检查名单中的每个人时,你都将其朋友加入名单。
在这里插入图片描述

6.3.1 查找最短路径
一度关系胜过二度关系,二度关系胜过三度关系,以此类推。
在这里插入图片描述

广度优先搜索不仅查找从A到B的路径,而且找到的是最短的路径。

只有按添加顺序查找时,才能实现这样的目的。
在这里插入图片描述

队列(queue):实现按添加顺序进行检查的数据结构。

6.3.2 队列
队列能够实现按添加顺序进行检查的需求。
队列不能随机访问,只能支持两种操作:入队和出队。
在这里插入图片描述

先加入的元素将先出队并被检查。

队列是一种先进先出(First In First Out)的数据结构;
栈是一种后进先出(Last In First Out)的数据结构。
在这里插入图片描述

6.4 实现图
图是由多个节点组成,需要使用代码来实现图。
每个结点都与邻近结点相连,散列表可以很好的实现这种关系。

散列表将键映射到值,比如将你这个节点映射到所有邻居。
在这里插入图片描述
在这里插入图片描述

表示这种映射关系的Python代码如下,

graph = {}  #前面提到Python中是用字典来实现散列表。{}表示生成空字典。
graph["you"] = ["alice", "bob", "claire"]
#"you"被映射到了一个数组,因此graph["you"]是一个数组,包含了"you"的所有邻居。 

在这里插入图片描述

graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []
#散列表是无序的,因此添加键-值对的顺序不关紧要。

有向图(directed graph):有一个节点指向相邻节点,单向关系。上图中Anuj,Peggy,Thom,Jonny都没有邻居,因为他们没有指向其他节点。

无向图(undirected graph):没有箭头,直接连接的节点互为邻居。
下面两图等效:
在这里插入图片描述

6.5 实现算法
队列实现原理
在这里插入图片描述

#首先使用函数deque来创建一个双端队列。
from collections import deque
def search(name):
	search_queue = deque() #创建一个队列
	search_queue += graph[name]  #将邻居都加入到这个搜索队列中。
	searched = []  #这个数组用于记录检查过的人,避免无限循环。
	
	#进行对每个人进行检查
	while search_queue: #只要队列不为空,
		person = search_queue.popleft() #就取出其中的第一个人(左端)
		if person not in searched:  #仅当这个人没检查过时才检查
			if person_is_seller(person): #检查此人是否是芒果销售商
				print(person + "is a mango seller!")
				return True
			else:
				search_queue += graph[person] #如果不是,将此人的朋友(相邻节点)都加入搜索队列。
				searched.append(person) #并将此人添加到以搜索过的列表,避免再次搜索此人。
	return False  #如果到达这里,说明队列中没有芒果销售商

#假设以姓名结尾为m的人为芒果销售商。
def person_is_seller(name):
	return name[-1] == 'm' #名字末尾为m
	
search("you") #函数调用

为什么要避免被重复搜索(检查):
上图中Peggy既是Alice的朋友又是Bob的朋友,因此她将被加入队列两次;因此,搜索队列将包含两个Peggy。

在这里插入图片描述

检查完一个人后,应将其标记为已检查,且不再检查;如果不这样做,就可能会导致无限循环。
在这里插入图片描述

因为搜索队列将在包含你和包含Peggy之间反复切换,从而造成无限循环。
在这里插入图片描述

列表是有序的。如果任务A依赖于任务B,在列表中任务A就必须在任务B后面,这被称为拓扑排序,使用它可根据图创建一个有序表。

6.5.1 树
树是特殊的图,其中没有往后指的边。
在这里插入图片描述

树是图的子集,树都是图,图不一定是树。

6.6 小结

  • 队列是先进先出。
  • 栈是后进先出。
  • 注意避免导致无限循环。

——持续修改完善中…

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
广度优先搜索是一种用于遍历或搜索的算法,它从起始节点开始,逐层向外遍历,直到找到目标节点或遍历完整张。具体实现过程中,可以使用队列来存储待遍历的节点,先将起始节点加入队列,然后依次取出队列中的节点,将其未访问过的邻居节点加入队列,直到队列为空或者找到目标节点为止。\n\广度优先搜索的时间复杂度为O(V+E),其中V为节点数,E为边数。在实际应用中,广度优先搜索常用于寻找最短路径、连通性检测等问题。\n\下面是一个简单的Pyth代码示例,演示了如何使用广度优先搜索遍历:\n\```pyth\from collections impor dequ\n\# 定义的邻接表表示\graph = {\ 'A' ['B', 'C'],\ 'B' ['A', 'D', 'E'],\ 'C' ['A', 'F'],\ 'D' ['B'],\ 'E' ['B', 'F'],\ 'F' ['C', 'E']\}\n\# 广度优先搜索函数\f bfs(graph, star, ):\ queu = dequ() # 定义队列\ queu.app(star) # 将起始节点加入队列\ visi = s() # 定义已访问集合\ visi.(star) # 将起始节点标记为已访问\n\ whi queu\ = queu.pplef() # 取出队列中的节点\ if == # 如果找到目标节点,返回Tru\ retur Tru\ for neighbor i graph[] # 遍历当前节点的邻居节点\ if neighbor i visi # 如果邻居节点未被访问过,加入队列并标记为已访问\ queu.app(ighbor)\ visi.(ighbor)\n\ retur Fals # 如果队列为空仍未找到目标节点,返回Fals\n\# 示例:在中查找从节点A到节点F的路径是否存在\pri(bfs(graph, 'A', 'F'))\n\```\n\

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值