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

作者:HibiscusToYou 时间:2021-01-31 08:14:23 

迷宫问题

问题描述:

迷宫可用方阵 [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()

来源:https://blog.csdn.net/qq_40772371/article/details/103938992

标签:Python,递归,迷宫
0
投稿

猜你喜欢

  • 详解Python绘图Turtle库

    2021-11-29 05:42:06
  • 修改asp代码防止被杀毒软件误删

    2007-10-07 12:32:00
  • 详解Python time库的使用

    2021-06-06 07:18:02
  • 浅谈Keras参数 input_shape、input_dim和input_length用法

    2021-02-19 13:24:40
  • 对python的文件内注释 help注释方法

    2021-12-20 18:12:46
  • python3批量删除豆瓣分组下的好友的实现代码

    2022-02-14 22:27:13
  • 使用AJAX的一个简单的例子

    2007-09-21 17:55:00
  • 未能加载文件或程序集“XXX”或它的某一个依赖项。试图加载格式不正确的程序。

    2023-07-01 00:38:21
  • Python使用微信itchat接口实现查看自己微信的信息功能详解

    2021-07-29 16:07:20
  • Python基于列表模拟堆栈和队列功能示例

    2021-08-17 01:36:14
  • python实现库存商品管理系统

    2023-06-01 06:37:29
  • php实现mysql同步的实现方法

    2023-11-24 13:58:56
  • 亚马逊购物用户体验分析(三)

    2009-10-25 12:53:00
  • Python爬虫之爬取二手房信息

    2021-08-11 19:40:50
  • DataFrame 数据合并实现(merge,join,concat)

    2022-03-28 04:24:02
  • 在VS2008中编译MYSQL5.1.48的方法

    2023-07-12 00:42:46
  • 详解pyenv下使用python matplotlib模块的问题解决

    2023-08-08 20:25:01
  • 一文带你探寻Python中的装饰器

    2021-07-11 10:17:59
  • 仿china.nba.com焦点图轮播展示效果(ie6,ff)

    2008-04-21 12:54:00
  • Python中字符串对象语法分享

    2022-04-19 14:48:34
  • asp之家 网络编程 m.aspxhome.com