内容概要
一、迷宫问题介绍
二、栈解决迷宫问题
三、队列解决迷宫问题
1、迷宫问题介绍
迷宫问题简单的说,就是通过一种算法,让计算机找到出口
迷宫可以通过二级列表实现,1表示路不允许通过;0表示路允许通过
比如下面的二级列表表示的迷宫
maze = [ # 横是y轴,纵是x轴
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 0, 1, 1, 0, 1],
[1, 1, 0, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 1, 1, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 1, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
解决迷宫问题有两种思路,一种是使用栈;另一种是使用队列
2、栈解决迷宫问题
栈解决迷宫问题,遵照深度优先。
也就是说一条路走到底,如果没能到达终点或者没有路走,那么就开始回溯,找到另外一条可以走的路。
栈解决迷宫问题代码实现
思路
(1)规定迷宫,迷宫的起点、终点
(2)创建栈,栈用于存储相关的路径坐标
(3)检查栈顶(所在迷宫的当前位置)的四个方向,只要有可通行的节点,将该节点进栈,重复步骤3
(4)如果四个方向都不能同行,并且没有到达终点,那么是死路,进行回退,栈顶出栈
(5)重复步骤3(特别要注意的是,想办法识别之前去过的节点,使得不再重复死路)
(6)找到终点
需要注意的是:
(1)如果不存在到达终点的路径,那么栈最终栈会变为空
(2)回退时,要使锝不再重复去过的路径,可以将去过的路径进行标记,告诉计算机这条路不通
(3)栈寻路是深度优先,找到的路径并非一定是最短的
代码实现
li = [
lambda x, y: (x, y+1),
lambda x, y: (x+1, y),
lambda x, y: (x, y-1),
lambda x, y: (x-1, y),
]
def search_path(maze, x1, y1, x2, y2): # x1, y1是起点坐标;x2, y2是终点坐标
stack = [(x1, y1)] # 使用列表充当一个栈,初始存有起点
while stack: # 栈空时,说明没有路径
node = stack[-1]
if node[0] == x2 and node[1] == y2: # 查看栈顶是否是终点
for node in stack:
print(node)
break
for func in li: # 检查4个方向是否可通过
next_node = func(node[0], node[1])
if maze[next_node[0]][next_node[1]] == 0:
maze[node[0]][node[1]] = 2 # 将前一个节点标记为已走过,避免回到之前的节点,陷入死循环
stack.append(next_node) # 更新所在位置
break
else:
maze[node[0]][node[1]] = 2 # 当四个方向都不可通行时,将所在位置设置为无法通行
stack.pop() # 回到上一个位置
else:
print("没有路径!")
search_path(maze, 1, 1, 8, 8)
3、队列解决迷宫问题
队列解决迷宫问题思路
**思路图解**
自己尝试写的代码(只实现了功能,并且似乎没有使用队列)
func_li = [
lambda x, y: (x, y+1),
lambda x, y: (x+1, y),
lambda x, y: (x, y-1),
lambda x, y: (x-1, y),
]
def search_path(maze, x1, y1, x2, y2):
nodes = [(x1, y1)]
maze[x1][y1] = 2
next_nodes = []
count = 3
n = 1
while len(nodes):
for node in nodes:
for func in func_li:
next_node = func(node[0], node[1])
if next_node[0] == x2 and next_node[1] == y2:
result_node = [next_node]
node = next_node
# while count > 2:
while not (node[0] == x1 and node[1] == y1):
for fu in func_li:
previous_node = fu(node[0], node[1])
if maze[previous_node[0]][previous_node[1]] == count-1:
result_node.append(previous_node)
node = previous_node
count -= 1
print(1)
break
print(result_node)
else:
# result_node.append((x1, y1))
return result_node
if maze[next_node[0]][next_node[1]] == 0:
next_nodes.append(next_node)
maze[next_node[0]][next_node[1]] = count
tmp = nodes
nodes = next_nodes
next_nodes = tmp
next_nodes.clear()
count += 1
for li in maze:
print(li)
print("=================={}===================".format(n))
n += 1
else:
print("不存在路径!")
re_li = search_path(maze, 1, 1, 8, 8)
print(re_li)
优化
def search_path(maze, x1, y1, x2, y2):
nodes = [(x1, y1)] # 定义一个列表用于存储本次需要探寻的节点
next_nodes = [] # 将由nodes探寻得到的可通过节点存储到next_nodes中
maze[x1][y1] = 2 # 将起点设置为已走
mark = 3 # 动态标记已经走过的节点
while len(nodes):
for node in nodes:
for func in li:
next_node = func(node[0], node[1])
# 判断终点和返回路径机制
if next_node[0] == x2 and next_node[1] == y2: # 判断节点是否为终点
result_node = [next_node] # 存储最终路径的列表,初始值为终点坐标
node = next_node
while not (node[0] == x1 and node[1] == y1): # 这里最难了,找到终点后要进行回溯,从终点节点按照之前设置的动态标记找回起点
for fu in li:
previous_node = fu(node[0], node[1])
if maze[previous_node[0]][previous_node[1]] == mark-1: # 根据标记找到正确的前一个节点
result_node.append(previous_node)
node = previous_node
mark -= 1 # 下一个标记的值比此次标记值小1
break
else:
result_node.reverse() # 由于是从终点回退到起点,所以最终的列表循序是反的
return result_node
if maze[next_node[0]][next_node[1]] == 0: # 判断该节点是否可以通过
next_nodes.append(next_node)
maze[next_node[0]][next_node[1]] = mark
tmp = nodes
nodes = next_nodes
next_nodes = tmp
next_nodes.clear()
mark += 1 # 标记值自增,用于区分不同次的遍历
# 查看标记
# for line in maze:
# print(line)
# print()
else:
print("不存在路径!")
牛逼的清华大佬的牛逼代码
from collections import deque
def search_path_queue(maze, x1, y1, x2, y2):
global cur_node
queue = deque([(x1, y1, -1)]) # 与自己的思路不同的是,大佬将用于找到上一个节点的标记添加每个节点里
path = []
while len(queue) > 0:
node = queue.pop()
path.append(node)
for func in li:
next_node = func(node[0], node[1])
if maze[next_node[0]][next_node[1]] == 0:
cur_node = (next_node[0], next_node[1], len(path) - 1)
queue.append(cur_node)
maze[next_node[0]][next_node[1]] = 2
if next_node[0] == x2 and next_node[1] == y2:
result_li = []
node = queue.pop()
while node[2] != -1:
result_li.append((node[0], node[1]))
node = path[node[2]]
else:
result_li.append((x1, y1))
result_li.reverse()
return result_li
else:
print("没有路径!")