python广度优先搜索得到两点间最短路径


Posted in Python onJanuary 17, 2019

前言

之前一直写不出来,这周周日花了一下午终于弄懂了, 顺便放博客里,方便以后忘记了再看看。
要实现的是输入一张 图,起点,终点,输出起点和终点之间的最短路径。

广度优先搜索

适用范围: 无权重的图,与深度优先搜索相比,深度优先搜索法占内存少但速度较慢,广度优先搜索算法占内存多但速度较快

复杂度: 时间复杂度为O(V+E),V为顶点数,E为边数

思路

广度优先搜索是以层为顺序,将某一层上的所有节点都搜索到了之后才向下一层搜索;
比如下图:

python广度优先搜索得到两点间最短路径

从0结点开始搜索的话,一开始是0、将0加入队列中;
然后下一层,0可以到达的有1,2,4,将他们加入队列中;
接下来是1,1能到达的且未被访问的是结点3
顺序就是 0, 1,2,4, 3,这里用下划线表示每一层搜索得到的结点;

每一次用cur = que[head]取出头指针指向的结点,并搜索它能到达的结点;因此,可以用一个队列que来保存已经访问过的结点,队列有头指针head以及尾指针tail,起点start与结点i有边并且结点i未被访问过,则将该结点加入队列中,tail指针往后移动;当tail等于顶点数时算法结束

对于每一次while循环,head都加一,也就是往右边移动,比如一开始head位置是0,下一层的时候head位置元素就为1,也就是搜索与结点1有边的且未被访问的结点

用一个数组book来标识结点i是否已经被访问过;用字典来保存起点到各个点的最短路径;
代码如下:

import numpy as np

ini_matrix = [
     [0, 1, 1, 0, 1],
     [1, 0, 0, 1, 0],
     [1, 0, 0, 0, 1],
     [0, 1, 0, 0, 0],
     [1, 0, 1, 0, 0]
     ]


def bfs(matrix_para, start_point_para, end_point_para):
  """
  广度优先搜索
  :param matrix_para 图
  :param start_point_para 起点
  :param end_point_para 终点
  :return: 返回关联度
  """
  matrix = matrix_para
  start_point = start_point_para
  end_point = end_point_para

  vertex_num = len(matrix) # 顶点个数

  que = np.zeros(vertex_num, dtype=np.int) # 队列, 用于存储遍历过的顶点
  book = np.zeros(vertex_num, dtype=np.int) # 标记顶点i是否已经被访问,1表被访问,0表未被访问

  point_step_dict = dict() # key:点,value:起点到该点的步长

  # 队列初始化
  head = 0
  tail = 0

  # 从起点出发,将起点加入队列
  que[tail] = start_point # 等号右边为顶点号(起点)
  tail += 1
  book[start_point] = 1 # book[i] i为顶点号

  while head<tail:
    cur = que[head]
    for i in range(vertex_num):
      # 判断从顶点cur到顶点i是否有边,并判断顶点i是否已经被访问过
      if matrix[cur][i] == 1 and book[i] == 0:
        que[tail] = i # 将顶点i放入队列中
        tail += 1 # tail指针往后移
        book[i] = 1 # 标记顶点i为已经访问过
        point_step_dict[i] = head + 1 # 记录步长
      if tail == vertex_num: # 说明所有顶点都被访问过
        break
    head += 1

  for i in range(tail):
    print(que[i])

  try:
    relevancy = point_step_dict[end_point]
    return relevancy
  except KeyError: # 捕获错误,如果起点不能到达end_point,则字典里没有这个键,返回None
    return None

result = bfs(ini_matrix, 1, 4)
print("result:", result)

错误

在经同学的一番调教之后,我深刻意识到了这段代码有个问题(不能用head记录步长),就是对于有环的时候,可能得到的步长(迭代次数)会比最短路径还大;
比如,起点为4,终点为3:这里每一遍迭代都是一次while循环
第一遍迭代,队列4,head指向4,步长为0
第二遍迭代,队列4,0 , 2,head指向0, 步长为1
第三遍迭代,队列4,0 , 2,1,head指向2,步长为2,
第四遍迭代,对于2,2周围都被访问过了,但此时head仍然+=1为3,这就导致了下一次的步长会比实际的步长多1
第五遍迭代, 3,步长为4

纠正

改进的思路:用count记录步长,flag用于标识当前搜索能到达的边的该结点cur = que[head]周围是否已经被访问过,False表示没有,True表示该结点i周围都被访问过了;也就是,当flag为False时,表示对于cur周围已经都访问过了,此时步长count不需要自增1;

import numpy as np

ini_matrix = [
     [0, 1, 1, 0, 1],
     [1, 0, 0, 1, 0],
     [1, 0, 0, 0, 1],
     [0, 1, 0, 0, 0],
     [1, 0, 1, 0, 0]
     ]


def bfs(matrix_para, start_point_para, end_point_para):
  """
  广度优先搜索
  :param matrix_para 图
  :param start_point_para 起点
  :param end_point_para 终点
  :return: 返回关联度
  """
  matrix = matrix_para
  start_point = start_point_para
  end_point = end_point_para

  vertex_num = len(matrix) # 顶点个数

  que = np.zeros(vertex_num, dtype=np.int) # 队列, 用于存储遍历过的顶点
  book = np.zeros(vertex_num, dtype=np.int) # 标记顶点i是否已经被访问,1表被访问,0表未被访问

  point_step_dict = dict() # key:点,value:起点到该点的步长

  # 队列初始化
  head = 0
  tail = 0

  # 迭代次数
  count = 0

  # 从0号顶点出发,将0号顶点加入队列
  que[tail] = start_point # 等号右边为顶点号(起点)
  tail += 1
  book[start_point] = 1 # book[i] i为顶点号

  while head<tail:
    flag = False # 用flag标识结点i是否周围都是被访问过的
    cur = que[head]
    for i in range(vertex_num):
      # 判断从顶点cur到顶点i是否有边,并判断顶点i是否已经被访问过
      if matrix[cur][i] == 1 and book[i] == 0:
        que[tail] = i # 将顶点i放入队列中
        tail += 1 # tail指针往后移
        book[i] = 1 # 标记顶点i为已经访问过
        point_step_dict[i] = count + 1 # 记录步长
        flag = True
      if tail == vertex_num: # 说明所有顶点都被访问过
        break
    if flag:
      count += 1
    head += 1

  for i in range(tail):
    print(que[i])

  try:
    relevancy = point_step_dict[end_point]
    return relevancy
  except KeyError:
    return None

result = bfs(ini_matrix, 3, 4)
print("result:", result)

写在后面

真的很抱歉, 第一次写这种算法博客结果出了这么大的问题,之前都是一些记录BUG的文章,还好同学及时和我说了,主要原因还是自己没有做那么多测试的问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现类继承实例
Jul 04 Python
python里将list中元素依次向前移动一位
Sep 12 Python
使用Python快速搭建HTTP服务和文件共享服务的实例讲解
Jun 04 Python
python 判断矩阵中每行非零个数的方法
Jan 26 Python
python快排算法详解
Mar 04 Python
PyQt5中QTableWidget如何弹出菜单的示例代码
Feb 23 Python
Python3 shelve对象持久存储原理详解
Mar 23 Python
TensorFlow2.1.0安装过程中setuptools、wrapt等相关错误指南
Apr 08 Python
pandas DataFrame运算的实现
Jun 14 Python
Java byte数组操纵方式代码实例解析
Jul 22 Python
python 将Excel转Word的示例
Mar 02 Python
python的列表生成式,生成器和generator对象你了解吗
Mar 16 Python
python游戏地图最短路径求解
Jan 16 #Python
python调用百度地图WEB服务API获取地点对应坐标值
Jan 16 #Python
Python3 关于pycharm自动导入包快捷设置的方法
Jan 16 #Python
在Pycharm中自动添加时间日期作者等信息的方法
Jan 16 #Python
解析Python的缩进规则的使用
Jan 16 #Python
在Pycharm中将pyinstaller加入External Tools的方法
Jan 16 #Python
浅谈python3.x pool.map()方法的实质
Jan 16 #Python
You might like
WordPress中设置Post Type自定义文章类型的实例教程
2016/05/10 PHP
PHP foreach遍历多维数组实现方式
2016/11/16 PHP
xml转json的js代码
2012/08/28 Javascript
Javascript 检测键盘按键信息及键码值对应介绍
2013/01/03 Javascript
jQuery检测鼠标左键和右键点击的方法
2015/03/17 Javascript
JavaScript对象数组如何按指定属性和排序方向进行排序
2016/06/15 Javascript
浅谈JavaScript 中有关时间对象的方法
2016/08/15 Javascript
老生常谈js-react组件生命周期
2017/05/02 Javascript
Element-UI Table组件上添加列拖拽效果实现方法
2018/04/14 Javascript
vuex与组件联合使用的方法
2018/05/10 Javascript
React中使用async validator进行表单验证的实例代码
2018/08/17 Javascript
从零开始搭建vue移动端项目到上线的步骤
2018/10/15 Javascript
微信小程序+云开发实现欢迎登录注册
2019/05/24 Javascript
vue 解决form表单提交但不跳转页面的问题
2019/10/30 Javascript
vue内置组件component--通过is属性动态渲染组件操作
2020/07/28 Javascript
[05:59]带你看看DPC的台前幕后
2021/03/11 DOTA
Python linecache.getline()读取文件中特定一行的脚本
2008/09/06 Python
python中常用检测字符串相关函数汇总
2015/04/15 Python
python实现爬虫统计学校BBS男女比例之数据处理(三)
2015/12/31 Python
python list排序的两种方法及实例讲解
2017/03/20 Python
apache部署python程序出现503错误的解决方法
2017/07/24 Python
CentOS7下python3.7.0安装教程
2018/07/30 Python
python模块导入的细节详解
2018/12/10 Python
详解Python使用Plotly绘图工具,绘制甘特图
2019/04/02 Python
原生python实现knn分类算法
2019/10/24 Python
MATLAB数学建模之画图汇总
2020/07/16 Python
英国Boots旗下太阳镜网站:Boots Designer Sunglasses
2018/07/07 全球购物
介绍一些UNIX常用简单命令
2014/11/11 面试题
前处理组长岗位职责
2014/03/01 职场文书
汽车广告策划方案
2014/05/31 职场文书
2015年父亲节活动总结
2015/02/12 职场文书
求职信内容一般写什么?
2015/03/20 职场文书
房产证明范本
2015/06/19 职场文书
MySQL 重命名表的操作方法及注意事项
2021/05/21 MySQL
Python调用腾讯API实现人脸身份证比对功能
2022/04/04 Python
vscode内网访问服务器的方法
2022/06/28 Servers