广度优先搜索算法BFS讲解以及python 实现
一.图简介
假设你居住在旧金山,要从双子峰前往金门大桥,你想乘公交车前往。
为找出换乘最少的乘车路线,你将使用怎样的算法?
金门大桥未突出,因此一步无法到达那里。两步能吗?
金门大桥未突出,两步步无法到达那里。三步能吗?
金门大桥突出了!因此从双子峰出发,可沿下面的路线三步到达金门大桥。
还有其他前往金门大桥的路线,但他们更远(需要四步)。这个算法发现,前往金门大桥的最短路径需要三步,这种问题被称为最短路径问题。
解决最短路径的算法被称为广度优先搜索。
需要两个步骤:
- 使用图来建立问题模型。
- 使用广度优先搜索解决问题。
1图
图模拟一组连接。假设你和你朋友正在玩牌,模拟下谁欠谁钱。
Alex欠Rama钱,Tom欠Adit钱,等等。图由节点(node)和边(edge)组成。
图由节点和边组成。一个节点可以由众多节点直接相连组成。这些节点被称为邻居。在前面的欠钱图中,Rama是Alex的邻居,Adit不是Alex的邻居,因为它们不直接相连,但Adit是Rama的邻居,又是Tom的邻居。
二.广度优先搜索
广度优先搜索通常用来解决两类问题:
- 从A点出发,有前往节点B的路径吗?
- 从A点出发,前往节点B的路径那条最短?
算例
本文通过算法来解决问题1。
假设你经营着一个芒果农场,需要寻找芒果销售商,以便将这些芒果卖给他。为此你可以在你的朋友中查找。
首先创建一个朋友名单,然后依次检查名单中的每一个人,看他是不是芒果销售商。
假设你的朋友没有人是芒果销售商,你必须在朋友的朋友中寻找。
检查名单中的每个人时,你都将其朋友加入名单
这样一来,你不仅在朋友中查找,还在朋友的朋友中查找。
上文解释了如何解决问题1,下面讲解如何通过算法来解决问题2。
谁是最近的芒果销售商,例如,朋友是一度关系,朋友的朋友是二度关系。
在你看来,一度关系胜过二度关系,首先在一度关系里寻找,如果没有芒果销售商,再在二度关系里寻找,以此类推。
广度优先搜索就是这样干的,首先检查一度关系,再检查二度关系。
在本题中,你可以这样理解,一度关系先于二度关系加入名单。你按顺序检查名单中的每一个人,看他是不是芒果销售商,然后再在二度关系里找。
本题需要运用到一个知识点:队列
队列是一种数据结构,具有先进先出的特点。例如排队上车,排在前面的先上车。
三.代码实现
1. 使用图来建立问题模型。
首先,需要用代码来实现图。图是有节点和邻近节点组成。来表示 [我:余登武] ,[我的邻居:小明,小白] 这种映射关系。这需要用到散列表。
散列表可以将键映射到值,在这里,将你映射到你的朋友。
python中用字典表示散列表。
graph={}
graph['you']=["alice","bob","claire"]
更大的图
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']=[]
2.使用广度优先搜索解决问题
算法流程
流程中用到了队列。队列是一种数据结构,具有先进先出的特点。例如排队上车,排在前面的先上车。python中采用deque来创建一个队列。
代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu(余登武)
# @Date : 2021/1/04
#@email:1344732766@qq.com#定义图
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']=[]#检查是否是芒果销售商。如果名字最后一个字母为m就是芒果销售商
def person_is_seller(name):return name[-1] =='m'#from collections import deque #导入队列
search_deque=deque()#创建一个队列
search_deque+=graph['you']
#print(search_deque) #deque(['alice', 'bob', 'claire'])#检查部分代码
try:while search_deque: # 只有队列不为空find = False # 定义是否找到person = search_deque.popleft() # 取出队列中的第一个人if person_is_seller(person):print(person + "是芒果销售商")find = Truebreakelse:search_deque += graph[person] # 将朋友的朋友添加进队列
finally:if find==False:print('没有找到')
运行结果
该段代码终止条件:
- 找到一位芒果商
- 队列变为空的,意味着你的朋友圈里没有芒果商。
代码优化
代码优化
peggy既是Alice的朋友又是Bob的朋友,因此她将被加入队列两次:一次是在添加Alice的朋友时,一次是在添加Bob的朋友时。因此搜索队列里包含了两个peggy。
我们可以只检查peggy一次,检查完一个人后,标记这个人为已检查。且不再检查他。如果不这样做,很快可能陷入死循环,如果你的朋友关系如图。
最后代码
#定义图,图中每个人的朋友关系都要写
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']=[]#检查是否是芒果销售商。如果名字最后一个字母为m就是芒果销售商
def person_is_seller(name):return name[-1] =='m'#检查是否是芒果商代码from collections import deque #导入队列
search_deque=deque()#创建一个队列def search(name):search_deque = deque() # 创建一个队列search_deque += graph[name]#将一级关系添加进队列,即自己的朋友searched=[]#定义是否检查try:while search_deque: # 只有队列不为空find = False # 定义是否找到person = search_deque.popleft() # 取出队列中的第一个人if person not in searched:#如果这个人没有被检查if person_is_seller(person):#如果这个人是芒果商print(person + "是芒果销售商")find = Truebreakelse: #如果不是芒果商search_deque += graph[person] # 将朋友的朋友添加进队列searched.append(person)#将这个人标记为已检查finally:if find == False:print('没有找到')search('you')
运行结果
打印最短路径
在上文中,我们找到了芒果供应商是thom,我们现在需要把you到thom的关系打印出来。
我这里采用倒排表的知识来实现
假设
原始字典为
{‘问题IID’:[关键词1,关键词2…],‘问题2ID’:[关键词2,关键词3…]…}
处理后的倒排表为
{‘关键词1’:[问题1ID],‘关键词2’:[问题1ID,问题2ID…}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu(余登武)
# @Date : 2021/1/04
#@email:1344732766@qq.com#定义图
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']=[]#所有问题组合起来的倒排表 result
result = {}
for i in graph.keys():left,rights=i,graph[i]for right in rights:if right in result.keys():result[right].append(left)else:result[right] = [left]
#print(result)
#得到的结果{'alice': ['you'], 'bob': ['you'], 'claire': ['you'], 'anuj': ['bob'], 'peggy': ['bob', 'alice'], 'thom': ['claire'], 'jonny': ['claire']}def print_searchname(name1,name2):#name1为芒果商的名字即终点,name2为起点parent=result[name1] #终点的上一级.格式['claire']parent=''.join(parent)#去除中括号 得到格式 claireprint('终点是{0}'.format(name1))while True:if parent !=name2:print("上一级是{0}".format(parent) )parent=result[parent]parent = ''.join(parent)if parent ==name2:print('{0}的上一级是终点{1}'.format(parent,name2))break
print_searchname('thom','you')
本文有借鉴书籍《算法图解》
作者:电气-余登武
广度优先搜索算法BFS讲解以及python 实现相关推荐
- 图的深度(DFS)/广度优先搜索算法(BFS)/Dijkstra
类比二叉树先序遍历与图深度优先搜索 在引入图的深度优先搜索之前,为了更加容易理解.先考究一种特殊的图---二叉树的深度优先搜索算法---即二叉树的递归遍历方法. 二叉树的前序遍历算法: void Tr ...
- Python实现 宽度/广度优先搜索算法, 深度优先搜索算法
Python实现 宽度/广度优先搜索算法, 深度优先搜索算法 1. 二叉树图 2. 宽度/广度优先搜索算法(Breadth First Search,BSF) 3. 深度优先搜索算法 4. 宽度/广度 ...
- 广度优先搜索算法(BFS)详解
参考:https://www.cnblogs.com/tianqizhi/p/9914539.html 广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的 ...
- DFS深度优先搜索算法/BFS广度优先搜索算法(c/c++)
深度优先搜索算法(DFS) 深度优先搜索算法思路:(有点贪心算法的意思) 1,从某个给定结点a出发,访问它 2,查找关于a的邻接点,查找到a的第一个邻接点b之后,对b结点进行DFS搜索,也就是对b结点 ...
- bfs广度优先搜索算法_图的广度优先搜索(BFS)
bfs广度优先搜索算法 What you will learn? 您将学到什么? How to implement Breath first search of a graph? 如何实现图的呼吸优先 ...
- 深度优先搜索算法(Depth-First-Search,DFS)与广度优先搜索算法(Breadth-First Search,BFS)理解
最近学习到了这两种经典的算法,谈一下自己的理解 深度优先搜索算法(Depth-First-Search,DFS) 这个算法会尽可能深的搜索树的分支.当节点v的所在边都己被探寻过,搜索将回溯到发现节点v ...
- 【BFS三维路径规划】基于matlab广度优先搜索算法无人机三维路径规划【含Matlab源码 270期】
一.获取代码方式 获取代码方式1: 通过订阅紫极神光博客付费专栏,凭支付凭证,私信博主,可获得此代码. 获取代码方式2: 完整代码已上传我的资源:[三维路径规划]基于matlab广度优先搜索算法无人机 ...
- 广度优先搜索(BFS)——抓住那头牛(POJ 4001)
本文将以(POJ 4001)抓住那头牛 为例,讲解经典算法广度优先搜索(BFS)的STL写法 在实际写算法中,怎么能不使用更快.更方便.更准确.更高效的C++ STL模板呢 相信很多人都了解过广度优先 ...
- 广度优先搜索算法(有向图和无向图)
广度优先搜索算法的思想:以v作为源点出发访问其他节点,首先使用队列存储节点,再从队列中取出节点,遍历查找v和其他连通的节点,将和v连通的节点并且未被访问过的节点入队,遍历完和v连通的节点:再从队列中取 ...
最新文章
- Redis 主库宕机如何快速恢复?面试必问!
- mybatis-plus入坑指南
- ABC Perl Programing - 回 2gua 短消息
- OpenCV图像处理实际案例(一)---图像倾斜矫正(仿射变换)和去边(轮廓查找+ROI提取)
- IO多路复用之select篇
- Javascript 深入学习循环
- ARM汇编伪指令 .word
- 图卷积神经网络(part3)--三个经典谱域图卷积模型
- 联想拯救者y7000电池耗电快_游戏新选择:联想2020款拯救者Y7000/R7000爆料
- 双向广搜 8数码问题
- 职场中不得不防的8类人
- Hive UDF开发
- golang 最小堆排序实现
- winrar64位怎么破解
- mysql 局域网数据库共享,SQL Server 2005 在局域网中共享数据库
- protoc ——protubuf编译后的可执行文件命令usage
- java中调用cmd命令被阻塞无法返回和继续执行
- 打开阿里云网页为空白的解决办法
- 测量数据的计算机处理实验报告,测量数据处理实验报告..doc
- 从《国产凌凌漆》看到《头号玩家》,你就能全面了解5G
热门文章
- 一分钟带你快速进入Nacos的世界,史上最简易教程!零基础也能看明白!谁反对?
- Spring Boot-场景启动器
- php 以-截取剩余的字符串_10分钟从PHP到Python
- 该按钮可以重启计算机,电脑里面总是自动重启的问题,应该怎么做?
- NUMA架构的CPU
- HTML/CSS学习笔记01【概念介绍、基本标签】
- Android复习01(内部存储、外部存储、SD卡存储、XML解析、Json解析、保存登录密码)
- IOS7使用原生API进行二维码和条形码的扫描
- 攻防世界-web-ics-07-从0到1的解题历程writeup
- Docker_DockerFile