通过迷宫问题简单学习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。

显然DFS是把所有点都走一遍,得到的结果一般不是最短路径,所以这题要用BFS解决。

广度优先搜索(BFS)

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 len(lt)-1

def find(l,x1,y1,x2,y2):
    que = deque() #新建队列
    que.append((x1,y1,-1))
    path = [] #走过点的坐标
    while len(que) != 0:
        now = que.popleft()  #注意要左出队
        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))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z.volcano

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值