通过迷宫问题简单学习DFS和BFS算法
Posted z.volcano
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过迷宫问题简单学习DFS和BFS算法相关的知识,希望对你有一定的参考价值。
关于这两种算的法介绍可以参考基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)
例题
例题地址:https://www.dotcpp.com/oj/problem1672.html
题目描述
小明置身于一个迷宫,请你帮小明找出从起点到终点的最短路程。
小明只能向上下左右四个方向移动。
输入
输入包含多组测试数据。输入的第一行是一个整数T,表示有T组测试数据。
每组输入的第一行是两个整数N和M(1<=N,M<=100)。
接下来N行,每行输入M个字符,每个字符表示迷宫中的一个小方格。
字符的含义如下:
‘S’:起点
‘E’:终点
‘-’:空地,可以通过
‘#’:障碍,无法通过
输入数据保证有且仅有一个起点和终点。
输出
对于每组输入,输出从起点到终点的最短路程,如果不存在从起点到终点的路,则输出-1。
样例输入
1
5 5
S-###
-----
##---
E#---
---##
样例输出
9
深度优先搜索(DFS)
思路
用栈
来实现,先把起点坐标压入栈,接着按照一个顺序(例如右→下→左→上
)来判断下一步往哪里走,如果下一步对应坐标的值是'
,即为可走,把走过的每一个坐标点都压入栈
。当无路可走时,再一步步回退,另寻一条路走,当栈顶元素是终点的坐标时,结束程序。
首先记录起点和终点的坐标
,接着在给定数据的外面加上一圈#
作为边界,这里规定横向为y轴,纵向为x轴,起点坐标是(1,1)
,终点是(4,1)
['#', '#', '#', '#', '#', '#', '#']
['#', 'S', '-', '#', '#', '#', '#']
['#', '-', '-', '-', '-', '-', '#']
['#', '#', '#', '-', '-', '-', '#']
['#', 'E', '#', '-', '-', '-', '#']
['#', '-', '-', '-', '#', '#', '#']
['#', '#', '#', '#', '#', '#', '#']
判断下一步是否可走的依据
是,对应位置的值是否是-
,把走过路径的值都标记成+
,方便退回来时另寻路的判断。
代码实现
T = int(input())
dirs = [
lambda x,y:(x+1,y), #向右
lambda x,y:(x,y+1), #向下
lambda x,y:(x-1,y), #向左
lambda x,y:(x,y-1) #向上
]
def find(l,x1,y1,x2,y2):
stack = [] #存放坐标的栈
stack.append((x1,y1))
while len(stack) > 0: #当栈空时说明无解
#到达终点
if stack[-1][0] == x2 and stack[-1][1] == y2:
return stack
now = stack[-1] #当前点的坐标)
for dir in dirs: #右下左上依次判断
next = dir(now[0],now[1])
if l[next[0]][next[1]] in ['-','E']:
stack.append(next)
l[next[0]][next[1]] = '+' #标记为已经走过
break
else: #四个方向都不能走,则需要回退
l[next[0]][next[1]] = '+'
stack.pop()
return -1
for li in range(T):
n,m = map(int,input().split())
lt = []
lt.append(list('#'*(m+2)))
for i in range(n):
l = list('#'+input()+'#')
if 'S' in l: #起点坐标
y1 = l.index('S'); x1 = i+1
if 'E' in l: #终点坐标
y2 = l.index('E'); x2 = i+1
lt.append(l)
lt.append(list('#'*(m+2)))
print(find(lt,x1,y1,x2,y2))
如果起点到终点之间有通路,则返回这条路径所经过的所有坐标,否则返回-1。
广度优先搜索(BFS)
思路
DFS是一条道走到黑,BFS则是多方向同时探索,可以用队列
实现。
先把起点坐标入队
,可记为点1,若在该点上有两个方向可以前进,则把点1出队
,点2、3入队
。如果点2对应有点4可以前进,点3对应有点5、6可以前进,则点2出队
,点4入队
;再点3出队
,点5、6入队
。同时每执行一次出队,进行一次计数,到达终点(队列空
)时返回路径长度。
代码实现
python有自带的用于队列操作的模块,详细操作可以查看deque官方文档,这里演示几种基础操作
from collections import deque
#新建一个队列,同时指定队列长度
que = deque(maxlen=20)
#入队,默认从右边加入
que.append(2)
que.append(2)
que.append(3)
#也可以从左边加入
que.appendleft(5)
que.appendleft(0)
print(que)
#deque([0, 5, 2, 2, 3], maxlen=20)
#从左边和右边出队
que.pop()
que.popleft()
print(que)
#deque([5, 2, 2], maxlen=20)
print(que.count(2)) #统计元素个数 2
#在某个位置插入元素
que.insert(1,45)
print(que)
#deque([5, 45, 2, 2], maxlen=20)
que.reverse() #翻转
que.remove(2) #删除某个元素,只会删除找到的第一个
que.clear() #清空队列
q2 = que.copy() #复制
然后是最终代码
from collections import deque
T = int(input())
dirs = [
lambda x,y:(x+1,y), #向右
lambda x,y:(x,y+1), #向下
lambda x,y:(x-1,y), #向左
lambda x,y:(x,y-1) #向上
]
def output(path):
now = path[-1] #最后一个点的坐标,即终点
lt = [] #从起点到终点经过的路径
while now[2] != -1:
lt.append((now[0],now[1]))
now = path[now[2]]
lt.append((now[0],now[1])) #存入起点坐标
lt.reverse()
return lt
def find(l,x1,y1,x2,y2):
que = deque() #新建队列
que.append((x1,y1,-1))
path = [] #走过点的坐标
while len(que) != 0:
now = que.pop()
path.append(now)
if now[0] == x2 and now[1] == y2:
return output(path)
for dir in dirs:
next = dir(now[0],now[1])
if l[next[0]][next[1]] in ['-','E']: #可走
l[next[0]][next[1]] = '+'
#len(path)-1是上一个点在path中所在位置的下标
que.append((next[0],next[1],len(path)-1))
else:
return -1
for li in range(T):
n,m = map(int,input().split())
lt = []
lt.append(list('#'*(m+2)))
for i in range(n):
l = list('#'+input()+'#')
if 'S' in l: #起点坐标
y1 = l.index('S'); x1 = i+1
if 'E' in l: #终点坐标
y2 = l.index('E'); x2 = i+1
lt.append(l)
lt.append(list('#'*(m+2)))
print(find(lt,x1,y1,x2,y2))
向上下左右这四个方向探索的顺序会直接影响结果,在上述代码的基础上稍加改动就可以实现最短路径。
以上是关于通过迷宫问题简单学习DFS和BFS算法的主要内容,如果未能解决你的问题,请参考以下文章