一.图简介

假设你居住在旧金山,要从双子峰前往金门大桥,你想乘公交车前往。

为找出换乘最少的乘车路线,你将使用怎样的算法?

金门大桥未突出,因此一步无法到达那里。两步能吗?

金门大桥未突出,两步步无法到达那里。三步能吗?

金门大桥突出了!因此从双子峰出发,可沿下面的路线三步到达金门大桥。

还有其他前往金门大桥的路线,但他们更远(需要四步)。这个算法发现,前往金门大桥的最短路径需要三步,这种问题被称为最短路径问题。
解决最短路径的算法被称为广度优先搜索。
需要两个步骤:

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

1图
图模拟一组连接。假设你和你朋友正在玩牌,模拟下谁欠谁钱。

Alex欠Rama钱,Tom欠Adit钱,等等。图由节点(node)和边(edge)组成。

图由节点和边组成。一个节点可以由众多节点直接相连组成。这些节点被称为邻居。在前面的欠钱图中,Rama是Alex的邻居,Adit不是Alex的邻居,因为它们不直接相连,但Adit是Rama的邻居,又是Tom的邻居。

二.广度优先搜索

广度优先搜索通常用来解决两类问题:

  1. 从A点出发,有前往节点B的路径吗?
  2. 从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('没有找到')

运行结果

该段代码终止条件:

  1. 找到一位芒果商
  2. 队列变为空的,意味着你的朋友圈里没有芒果商。

代码优化
代码优化
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 实现相关推荐

  1. 图的深度(DFS)/广度优先搜索算法(BFS)/Dijkstra

    类比二叉树先序遍历与图深度优先搜索 在引入图的深度优先搜索之前,为了更加容易理解.先考究一种特殊的图---二叉树的深度优先搜索算法---即二叉树的递归遍历方法. 二叉树的前序遍历算法: void Tr ...

  2. Python实现 宽度/广度优先搜索算法, 深度优先搜索算法

    Python实现 宽度/广度优先搜索算法, 深度优先搜索算法 1. 二叉树图 2. 宽度/广度优先搜索算法(Breadth First Search,BSF) 3. 深度优先搜索算法 4. 宽度/广度 ...

  3. 广度优先搜索算法(BFS)详解

    参考:https://www.cnblogs.com/tianqizhi/p/9914539.html 广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的 ...

  4. DFS深度优先搜索算法/BFS广度优先搜索算法(c/c++)

    深度优先搜索算法(DFS) 深度优先搜索算法思路:(有点贪心算法的意思) 1,从某个给定结点a出发,访问它 2,查找关于a的邻接点,查找到a的第一个邻接点b之后,对b结点进行DFS搜索,也就是对b结点 ...

  5. bfs广度优先搜索算法_图的广度优先搜索(BFS)

    bfs广度优先搜索算法 What you will learn? 您将学到什么? How to implement Breath first search of a graph? 如何实现图的呼吸优先 ...

  6. 深度优先搜索算法(Depth-First-Search,DFS)与广度优先搜索算法(Breadth-First Search,BFS)理解

    最近学习到了这两种经典的算法,谈一下自己的理解 深度优先搜索算法(Depth-First-Search,DFS) 这个算法会尽可能深的搜索树的分支.当节点v的所在边都己被探寻过,搜索将回溯到发现节点v ...

  7. 【BFS三维路径规划】基于matlab广度优先搜索算法无人机三维路径规划【含Matlab源码 270期】

    一.获取代码方式 获取代码方式1: 通过订阅紫极神光博客付费专栏,凭支付凭证,私信博主,可获得此代码. 获取代码方式2: 完整代码已上传我的资源:[三维路径规划]基于matlab广度优先搜索算法无人机 ...

  8. 广度优先搜索(BFS)——抓住那头牛(POJ 4001)

    本文将以(POJ 4001)抓住那头牛 为例,讲解经典算法广度优先搜索(BFS)的STL写法 在实际写算法中,怎么能不使用更快.更方便.更准确.更高效的C++ STL模板呢 相信很多人都了解过广度优先 ...

  9. 广度优先搜索算法(有向图和无向图)

    广度优先搜索算法的思想:以v作为源点出发访问其他节点,首先使用队列存储节点,再从队列中取出节点,遍历查找v和其他连通的节点,将和v连通的节点并且未被访问过的节点入队,遍历完和v连通的节点:再从队列中取 ...

最新文章

  1. Redis 主库宕机如何快速恢复?面试必问!
  2. mybatis-plus入坑指南
  3. ABC Perl Programing - 回 2gua 短消息
  4. OpenCV图像处理实际案例(一)---图像倾斜矫正(仿射变换)和去边(轮廓查找+ROI提取)
  5. IO多路复用之select篇
  6. Javascript 深入学习循环
  7. ARM汇编伪指令 .word
  8. 图卷积神经网络(part3)--三个经典谱域图卷积模型
  9. 联想拯救者y7000电池耗电快_游戏新选择:联想2020款拯救者Y7000/R7000爆料
  10. 双向广搜 8数码问题
  11. 职场中不得不防的8类人
  12. Hive UDF开发
  13. golang 最小堆排序实现
  14. winrar64位怎么破解
  15. mysql 局域网数据库共享,SQL Server 2005 在局域网中共享数据库
  16. protoc ——protubuf编译后的可执行文件命令usage
  17. java中调用cmd命令被阻塞无法返回和继续执行
  18. 打开阿里云网页为空白的解决办法
  19. 测量数据的计算机处理实验报告,测量数据处理实验报告..doc
  20. 从《国产凌凌漆》看到《头号玩家》,你就能全面了解5G

热门文章

  1. 一分钟带你快速进入Nacos的世界,史上最简易教程!零基础也能看明白!谁反对?
  2. Spring Boot-场景启动器
  3. php 以-截取剩余的字符串_10分钟从PHP到Python
  4. 该按钮可以重启计算机,电脑里面总是自动重启的问题,应该怎么做?
  5. NUMA架构的CPU
  6. HTML/CSS学习笔记01【概念介绍、基本标签】
  7. Android复习01(内部存储、外部存储、SD卡存储、XML解析、Json解析、保存登录密码)
  8. IOS7使用原生API进行二维码和条形码的扫描
  9. 攻防世界-web-ics-07-从0到1的解题历程writeup
  10. Docker_DockerFile