Python 实现递归法解决迷宫问题的示例代码


Posted in Python onJanuary 12, 2020

迷宫问题

问题描述:

迷宫可用方阵 [m, n] 表示,0 表示可通过,1 表示不能通过。若要求左上角 (0, 0) 进入,设计算法寻求一条能从右下角 (m-1, n-1) 出去的路径。

示例图:

Python 实现递归法解决迷宫问题的示例代码

此示例图基本参数为:

  • m:对应
  • x 轴n:对应 y 轴
  • 绿色线代表期望输出的路径

算法思路

  1. 标记当前所在位置
  2. 如果此时所在位置为终点,说明可以到达终点,退出递归;

否则,则存在 4 种可能的移动方向即上、下、左、右,遍历这 4 个方向,如果这 4 个方向存在相邻值为 0 的点,则将当前点坐标标记为该相邻值为 0 的点坐标,进入递归

直观理解为:

Python 实现递归法解决迷宫问题的示例代码

上图中红色圈的相邻值为 0 的点有 3 个,则会依次遍历这 3 个点寻求某一条件并进入递归

实现过程

标记函数

def mark(maze, pos):
  """
  标记函数,用来标记历史走过的位置
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param pos: 当前需要标记的位置坐标 pos = (x, y),x = pos[0], y = pos[1]
  """
  maze[pos[0]][pos[1]] = 2 # 将走过的位置标记为 2

移动函数

def move(maze, pos):
  """
  移动函数,用来测试当前位置是否可继续移动,移动条件为当前位置为 0
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param pos: 当前需要标记的位置坐标 pos = (x, y),x = pos[0], y = pos[1]
  :return: bool 类型
  """
  return maze[pos[0]][pos[1]] == 0

核心函数 - 路径查找函数

def find_path(maze, start, end):
  """
  路径查找函数
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param start: 起始点位置坐标,start = (1, 1)
  :param end: 终点坐标,end = (m, n)
  :return: bool 类型
  """
  mark(maze, start) # 将起始位置标记
  if start == end: # 路径查找(递归)终止条件为到达终点
    move_path.append(start)
    return True

  # 未到达终点时,存在 4 种可能的移动方向,即上 (-1, 0),下 (1, 0),左 (0, -1),右 (0, 1)
  move_direction = [
    (-1, 0), (1, 0), (0, -1), (0, 1)
  ]
  direction = ['↑', '↓', '←', '→']
  for i in range(4): # 遍历 4 种可能的方向
    next_start = (start[0] + move_direction[i][0], start[1] + move_direction[i][1]) # 下一个可能的起始点坐标
    if move(maze, next_start): # 找出存在 0 即可移动的下一个起始点坐标,进入递归
      if find_path(maze, next_start, end):
        # 这里之所以仍然添加起始点坐标是因为当查询到下一个位置就是终点或者可到达终点时记录此时位置
        move_path.append(start)
        path_direction.append(direction[i]) # 记录路径方向
        return True
  return False # 遍历递归了 4 种可能方向后仍不能到达终点则说明无法走出迷宫

算法到这里基本上已经算完成,整体上不算太复杂

美化输出

生成带有移动路径数据的迷宫矩阵

def path_maze(maze, directions_map):
  """
  生成带有移动路径的迷宫矩阵
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param directions_map: 一个记录移动方向坐标的字典,有 ↑,↓,←,→ 4 个元素
  :return: path_maze
  """
  n, m = len(maze[0]), len(maze)
  for x in range(1, m-1):
    for y in range(1, n-1):
      maze[x][y] = maze[x][y] if maze[x][y] != 2 else 0 # 将标记的 2 还原为 0

  for x in range(m):
    for i in range(1, 2 * n - 1, 2):
      maze[x].insert(i, '  ') # 重初始化 maze,在每两个元素间插入占位符 '  ' 3 个空格

  for x in range(1, 2 * m - 1, 2):
    maze.insert(x, [' ', '  '] * (n-1) + ['']) # 插入两种空格占位符 ' ' 和 '  '

  for direction in directions_map:
    for directions_position in directions_map[direction]:
      i, j = directions_position
      i = 2 * i
      j = 2 * j
      if direction == "↑":
        maze[i - 1][j] = "↑"
      if direction == "↓":
        maze[i + 1][j] = "↓"
      if direction == "←":
        maze[i][j] = " ← "
      if direction == "→":
        maze[i][j + 1] = " → "
  return maze

生成的带路径数据的迷宫矩阵部分数据截图如下:

Python 实现递归法解决迷宫问题的示例代码

美化打印迷宫矩阵

def print_maze(maze, text='原始迷宫为:', end1='  ', end2='\n\n', xs=0, xe=0, ys=0, ye=0):
  """
  输出迷宫矩阵,非必要,可注释删除
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param text: 输出提示
  :param end1: 控制每行尾结束符
  :param end2: 控制每行尾结束符
  :param xs: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param xe: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param ys: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param ye: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  """
  print(text)
  n, m = len(maze[0]), len(maze)
  for x in range(xs, m-xe):
    for y in range(ys, n-ye):
      print(maze[x][y], end=end1)
    print(end=end2)

最终输出结果:

Python 实现递归法解决迷宫问题的示例代码

效果尚可

完整代码

# -*- coding: utf-8 -*-
"""
Created on 2020/1/11 10:51
Author : zxt
File  : maze_recursion.py
Software: PyCharm
"""


from random import randint


def mark(maze, pos):
  """
  标记函数,用来标记历史走过的位置
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param pos: 当前需要标记的位置坐标 pos = (x, y),x = pos[0], y = pos[1]
  """
  maze[pos[0]][pos[1]] = 2 # 将走过的位置标记为 2


def move(maze, pos):
  """
  移动函数,用来测试当前位置是否可继续移动,移动条件为当前位置为 0
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param pos: 当前需要标记的位置坐标 pos = (x, y),x = pos[0], y = pos[1]
  :return: bool 类型
  """
  return maze[pos[0]][pos[1]] == 0


move_path = [] # 记录能成功到达出口的移动路径坐标
path_direction = [] # 记录能成功到达出口的移动路径方向


def find_path(maze, start, end):
  """
  路径查找函数
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param start: 起始点位置坐标,start = (1, 1)
  :param end: 终点坐标,end = (m, n)
  :return: bool 类型
  """
  mark(maze, start) # 将起始位置标记
  if start == end: # 路径查找(递归)终止条件为到达终点
    move_path.append(start)
    return True

  # 未到达终点时,存在 4 种可能的移动方向,即上 (-1, 0),下 (1, 0),左 (0, -1),右 (0, 1)
  move_direction = [
    (-1, 0), (1, 0), (0, -1), (0, 1)
  ]
  direction = ['↑', '↓', '←', '→']
  for i in range(4): # 遍历 4 种可能的方向
    next_start = (start[0] + move_direction[i][0], start[1] + move_direction[i][1]) # 下一个可能的起始点坐标
    if move(maze, next_start): # 找出存在 0 即可移动的下一个起始点坐标,进入递归
      if find_path(maze, next_start, end):
        # 这里之所以仍然添加起始点坐标是因为当查询到下一个位置就是终点或者可到达终点时记录此时位置
        move_path.append(start)
        path_direction.append(direction[i]) # 记录路径方向
        return True
  return False # 遍历递归了 4 种可能方向后仍不能到达终点则说明无法走出迷宫


def gen_maze(m, n):
  """
  生成随机迷宫阵列
  :param m: int 类型
  :param n: int 类型
  :return: maze
  """
  m += 2
  n += 2 # m 和 n 均 +2 是为了构造最外层的 1
  maze = [[1 for i in range(n)] for j in range(m)] # 初始化大小为 m * n,值全为 1 的二维矩阵
  for x in range(1, m-1):
    for y in range(1, n-1):
      """
      这里 x, y 取值范围为 x ∈ [1, m-1),y ∈ [1, n-1) 是因为我们令此迷宫的最外层(四周)均为 1,如:
      考察 3 * 3 矩阵,一种可能的阵列为:
      [
       _ |←--- n:y ---→|
       ↑ [1, 1, 1, 1, 1],
       | [1, 0, 1, 0, 1],
      m:x [1, 0, 0, 1, 1],
       | [1, 1, 0, 0, 1],
       ↓ [1, 1, 1, 1, 1] 
      ]
      """
      if (x == 1 and y == 1) or (x == m - 2 and y == n - 2):
        maze[x][y] = 0 # 起始点和终点必为 0
      else:
        maze[x][y] = randint(0, 1) # 在最外层均为 1 的情况下内部随机取 0,1
  return maze


def print_maze(maze, text='原始迷宫为:', end1='  ', end2='\n\n', xs=0, xe=0, ys=0, ye=0):
  """
  输出迷宫矩阵,非必要,可注释删除
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param text: 输出提示
  :param end1: 控制每行尾结束符
  :param end2: 控制每行尾结束符
  :param xs: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param xe: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param ys: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  :param ye: 控制是否输出最上方的 1 环,0 为输出,1 为不输出
  """
  print(text)
  n, m = len(maze[0]), len(maze)
  for x in range(xs, m-xe):
    for y in range(ys, n-ye):
      print(maze[x][y], end=end1)
    print(end=end2)


def path_maze(maze, directions_map):
  """
  生成带有移动路径的迷宫矩阵
  :param maze: 一个 m*n 大小的二维矩阵迷宫
  :param directions_map: 一个记录移动方向坐标的字典,有 ↑,↓,←,→ 4 个元素
  :return: path_maze
  """
  n, m = len(maze[0]), len(maze)
  for x in range(1, m-1):
    for y in range(1, n-1):
      maze[x][y] = maze[x][y] if maze[x][y] != 2 else 0 # 将标记的 2 还原为 0

  for x in range(m):
    for i in range(1, 2 * n - 1, 2):
      maze[x].insert(i, '  ') # 重初始化 maze,在每两个元素间插入占位符 '  ' 3 个空格

  for x in range(1, 2 * m - 1, 2):
    maze.insert(x, [' ', '  '] * (n-1) + ['']) # 插入两种空格占位符 ' ' 和 '  '

  for direction in directions_map:
    for directions_position in directions_map[direction]:
      i, j = directions_position
      i = 2 * i
      j = 2 * j
      if direction == "↑":
        maze[i - 1][j] = "↑"
      if direction == "↓":
        maze[i + 1][j] = "↓"
      if direction == "←":
        maze[i][j] = " ← "
      if direction == "→":
        maze[i][j + 1] = " → "
  return maze


def main():
  # maze = gen_maze(m=10, n=12)
  maze = \
    [
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
      [1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1],
      [1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
      [1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1],
      [1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1],
      [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1],
      [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
      [1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1],
      [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1],
      [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
      [1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1],
      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    ] # 输入样式矩阵,这里最外层用 1 环包围住,目的是方便后续的处理,可以用 gen_maze() 函数自生成
  print_maze(maze)
  if find_path(maze, start=(1, 1), end=(10, 12)):
    mp = move_path[::-1]
    pd = path_direction[::-1]
    # 这里 pos[0] 和 pos[1] 都要 -1 是因为原来的递归计算中存在最外层的 1 环
    print('坐标移动顺序为:', [(pos[0]-1, pos[1]-1) for pos in mp])
    path_direction_map = {
      '↑': [],
      '↓': [],
      '←': [],
      '→': []
    } # 路径方向的映射表
    for i in range(len(pd)):
      path_direction_map[pd[i]].append(mp[i])
    maze = path_maze(maze, path_direction_map)
    print_maze(maze, text='迷宫移动路径为:', end1='', end2='\n', xs=1, xe=1, ys=1, ye=1)
  else:
    print('此迷宫无解')


if __name__ == '__main__':
  main()

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

Python 相关文章推荐
Nginx搭建HTTPS服务器和强制使用HTTPS访问的方法
Aug 16 Python
Python内建模块struct实例详解
Feb 02 Python
windows下python和pip安装教程
May 25 Python
Python高级特性切片(Slice)操作详解
Sep 27 Python
python binascii 进制转换实例
Jun 12 Python
python3 中的字符串(单引号、双引号、三引号)以及字符串与数字的运算
Jul 18 Python
django云端留言板实例详解
Jul 22 Python
关于PyTorch源码解读之torchvision.models
Aug 17 Python
Python 点击指定位置验证码破解的实现代码
Sep 11 Python
python异步编程 使用yield from过程解析
Sep 25 Python
对python pandas中 inplace 参数的理解
Jun 27 Python
linux系统下pip升级报错的解决方法
Jan 31 Python
Python3.x+迅雷x 自动下载高分电影的实现方法
Jan 12 #Python
tensorflow的计算图总结
Jan 12 #Python
python利用JMeter测试Tornado的多线程
Jan 12 #Python
Django 批量插入数据的实现方法
Jan 12 #Python
python处理RSTP视频流过程解析
Jan 11 #Python
pyftplib中文乱码问题解决方案
Jan 11 #Python
python实现实时视频流播放代码实例
Jan 11 #Python
You might like
PHP实现Socket服务器的代码
2008/04/03 PHP
PHP无刷新上传文件实现代码
2011/09/19 PHP
关于PHP的curl开启问题探讨
2014/04/08 PHP
php用正则判断是否为数字的方法
2016/03/25 PHP
让ThinkPHP的模板引擎达到最佳效率的方法详解
2017/03/14 PHP
ThinkPHP3.2框架使用addAll()批量插入数据的方法
2017/03/16 PHP
JS判断是否为数字,是否为整数,是否为浮点数的代码
2010/04/24 Javascript
extjs 初始化checkboxgroup值的代码
2011/09/21 Javascript
分享jQuery插件的学习笔记
2016/01/14 Javascript
javascript验证内容为数字以及长度为10的简单实例
2016/08/20 Javascript
Web性能优化系列 10个提升JavaScript性能的技巧
2016/09/27 Javascript
js初始化验证实例详解
2016/11/26 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
2017/03/30 Javascript
jQuery实现的鼠标响应缓冲动画效果示例
2018/02/13 jQuery
Vue 指令实现按钮级别权限管理功能
2019/04/23 Javascript
详解vuex之store源码简单解析
2019/06/13 Javascript
如何实现小程序与小程序之间的跳转
2020/11/04 Javascript
vue绑定class的三种方法
2020/12/24 Vue.js
[01:18:21]EG vs TNC Supermajor小组赛B组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
python版本的读写锁操作方法
2016/04/25 Python
python递归打印某个目录的内容(实例讲解)
2017/08/30 Python
关于Python如何避免循环导入问题详解
2017/09/14 Python
python3爬取数据至mysql的方法
2018/06/26 Python
对python实时得到鼠标位置的示例讲解
2018/10/14 Python
python获取txt文件词向量过程详解
2019/07/05 Python
Django用数据库表反向生成models类知识点详解
2020/03/25 Python
Python格式化输出--%s,%d,%f的代码解析
2020/04/29 Python
python中wx模块的具体使用方法
2020/05/15 Python
python实现邮件循环自动发件功能
2020/09/11 Python
Django执行源生mysql语句实现过程解析
2020/11/12 Python
为什么使用接口?
2014/08/13 面试题
如何拷贝一整个Java对象,包括它的状态
2013/12/27 面试题
经理秘书岗位职责
2013/11/14 职场文书
2014年财务工作自我评价
2014/09/23 职场文书
督导岗位职责
2015/02/04 职场文书
资深HR教你写好简历中的自我评价
2019/05/07 职场文书