# 第一种:使用回溯法的子集树模版实现 #定义迷宫布局,1表示墙壁,0表示可通行的路径,这里是一个8行10列的迷宫示例 maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 0, 1, 1, 1, 1, 0, 1], [1, 1, 0, 1, 0, 1, 1, 0, 1, 1], [1, 0, 1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 1, 1, 0, 1, 1], [1, 1, 0, 1, 1, 1, 1, 1, 0, 1], [1, 0, 1, 0, 0, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 0, 1, 1, 1, 1]] # 迷宫的行数和列数 m, n = 8, 10 # 迷宫入口坐标,这里表示第2行第1列(索引从0开始) entry = (1, 0) # 用于记录一个解(路径),初始化为包含入口坐标的列表 path = [entry] # 用于存储所有找到的解(路径集合) paths = [] # 移动的方向(顺时针8个:北(N),东北(EN),东(E),东南(ES),南(S),西南(WS),西(W),西北(WN)) # 每个元组表示在行列上的偏移量,例如 (-1, 0) 表示向上移动一格(行减1,列不变) directions = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)] # 检查坐标 (nx, ny) 是否冲突(即是否越界或者是墙壁) def conflict(nx, ny): global m, n, maze # 判断坐标是否在迷宫范围内(行和列都要满足大于等于0且小于迷宫的行数和列数) # 并且对应位置的值为0(表示可通行),如果满足条件则返回False,表示没有冲突 if 0 <= nx < m and 0 <= ny < n and maze[nx][ny] == 0: return False return True # 递归函数,用于在迷宫中探索可行路径 def walk(x, y): global entry, m, n, maze, path, paths, directions # 判断当前位置是否不是入口并且是否到达了迷宫的边界(行或列索引是最后一行或最后一列) # 如果是,则将当前路径path添加到paths列表中,表示找到了一条可行路径 if (x, y)!= entry and (x % (m - 1) == 0 or y % (n - 1) == 0): paths.append(path[:]) else: # 遍历所有的移动方向 for d in directions: # 根据当前方向计算下一个位置的坐标 nx, ny = x + d[0], y + d[1] # 将下一个位置坐标添加到当前路径path中,表示尝试往这个方向走 path.append((nx, ny)) # 检查下一个位置是否没有冲突(即可通行) if not conflict(nx, ny): # 如果可通行,将该位置标记为已走过(这里标记为2,方便后续区分) maze[nx][ny] = 2 # 递归调用walk函数,继续从新位置探索路径 walk(nx, ny) # 探索完该位置后,将其标记恢复为0(可通行状态),以便下次探索其他路径时可以再次经过 maze[nx][ny] = 0 # 将刚刚添加的位置坐标从当前路径path中移除,回溯到上一个位置,尝试其他方向 path.pop() # 用于展示给定路径在迷宫中的情况,同时打印原始迷宫和标记了路径后的迷宫 def show(path): global maze import pprint, copy # 深拷贝原始迷宫,用于标记路径后展示,不影响原始迷宫数据 maze2 = copy.deepcopy(maze) for p in path: # 在拷贝的迷宫中,将路径上的位置标记为2 maze2[p[0]][p[1]] = 2 # 打印原始迷宫布局 pprint.pprint(maze) print() # 打印标记了路径后的迷宫布局 pprint.pprint(maze2) # 从入口位置 (1, 0) 开始在迷宫中探索路径 walk(1, 0) # 判断是否找到了路径,如果找到了则进行后续操作(打印路径和展示路径在迷宫中的情况) if paths: print(paths[-1], '\n') show(paths[-1]) else: print("没有找到可行的迷宫路径")
# 第二种:使用矩阵标记移动路径 class Maze(): def __init__(self): """ 类的构造函数,用于初始化迷宫相关的属性,这里将迷宫的边长属性N初始化为4,表示处理的是一个4x4的迷宫(示例情况)。 """ self.N = 4 def printSolution(self, sol): """ 以清晰易读的格式打印出迷宫的解(路径)情况。 参数sol是一个二维列表,其中用1表示路径上的位置,0表示不在路径上的位置,以此来展示整个迷宫中可行的路径布局。 该函数通过两层嵌套的for循环遍历二维列表sol,逐行逐列地打印出每个元素,元素之间用空格隔开,每行结束后进行换行操作。 """ for i in range(self.N): for j in range(self.N): print(sol[i][j], end=' ') print() def isSafe(self, maze, x, y): """ 检查给定坐标 (x, y) 在迷宫中是否是安全可通行的位置。 参数: - maze: 二维列表,表示整个迷宫的布局,其中1表示可通行的位置(比如通道),0表示不可通行的位置(比如墙壁)。 - x, y: 待检查的位置坐标,其坐标值的范围应该在迷宫的边长范围内。 返回值: - 如果坐标 (x, y) 满足以下所有条件,则返回True,表示该位置是安全可通行的;否则返回False: - x坐标大于等于0且小于迷宫的边长self.N。 - y坐标大于等于0且小于迷宫的边长self.N。 - 迷宫中对应坐标位置的元素值为1,即表示该位置是可通行的。 """ return x >= 0 and x < self.N and y >= 0 and y < self.N and maze[x][y] == 1 def getPath(self, maze, x, y, sol): """ 通过递归回溯的方式尝试在迷宫中寻找从起点 (0, 0) 到终点 (self.N - 1, self.N - 1) 的可行路径。 参数: - maze: 二维列表,代表整个迷宫的布局,元素值1和0分别表示可通行与不可通行位置。 - x, y: 当前正在探索的位置坐标,初始时为起点坐标 (0, 0),在递归过程中会不断更新。 - sol: 二维列表,用于记录在探索过程中发现的路径情况,初始化为全部元素为0的列表,若某个位置在找到的路径上,则对应元素会被标记为1。 返回值: - 如果成功找到从起点到终点的路径,返回True;如果经过所有尝试后仍未找到可行路径,则返回False。 """ if x == self.N - 1 and y == self.N - 1: sol[x][y] = 1 return True if self.isSafe(maze, x, y): sol[x][y] = 1 # 优先尝试向右移动一格(y坐标增加1)来探索路径 if self.getPath(maze, x, y + 1, sol): return True # 若向右探索无果,再尝试向下移动一格(x坐标增加1)来探索路径 if self.getPath(maze, x + 1, y, sol): return True sol[x][y] = 0 return False return False if __name__ == "__main__": rat = Maze() # 定义一个4x4的迷宫布局示例,其中1表示可通行位置,0表示不可通行位置(如墙壁等) maze = [[1, 0, 0, 0], [1, 1, 0, 1], [0, 1, 0, 0], [1, 1, 1, 1]] # 初始化用于记录路径情况的二维列表,初始所有位置都标记为0,表示尚未探索到路径经过这些位置 sol = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] if not rat.getPath(maze, 0, 0, sol): print("不存在可达路径!") else: rat.printSolution(sol)
返回结果:
第一种:
第二种: