通过迷宫问题简单学习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算法的主要内容,如果未能解决你的问题,请参考以下文章

通过迷宫问题简单学习DFS和BFS算法

〔C++算法分析〕迷宫问题

为啥bfs走迷宫的路程是最小值而dfs就不一定

广度优先搜索解决迷宫问题

算法浅谈——走迷宫问题与广度优先搜索

算法学习笔记 二叉树和图遍历—深搜 DFS 与广搜 BFS