算法小课堂:走迷宫之DFS vs BFS

前言

  上期我们说到DFS的经典入门题目——Fibonaci数列的第n项,这次我们来说下DFS或者BFS较为经典的走迷宫题目解法,很多关于迷宫类题目的母题都是这个了,而且也很容易理解,冲冲冲!

  我们约定迷宫里出口用S表示,出口用T表示,可以通行(不是一方通行)的格子用点表示,障碍物(即不能通行)的格子用*表示。

  类似如下的输入形式:

....s*
.*****
....*.
***..*
.T....

  今天我们利用两种思路,分别是DFS和BFS来解决这个问题。

优快云 (゜-゜)つロ 干杯

DFS走迷宫

算法思路

  DFS方法来解决的思路类似于我们上次说的老鼠走迷宫,真就无限套娃呗……从一条路一直走到底A,如果碰到障碍物,就退回一步到B,在B处尝试完所有的可能方向,如果还不行,继续回退到C,循环往复,直到找到可行的通路。如果没有,则表示没有通路。

  这里有几个点需要注意:

  1. 怎么表示走的过程,实际上就是你在二维数组操作的过程
  2. 走的过程中坐标移动时不应该超过地图范围
  3. 迷宫可能有多条通路,应该选择最近的一条
  4. 回退过程中怎么标记已经查看过的节点避免循环bug
  5. 最后一个是我们整体的逻辑思路:即上面一段话的程序实现

  因为是开始讲解DFS的算法题解,我会尽量详细,让大家没有太多理解负担,本身也是为了通俗易懂的讲解算法知识。


代码实现细节

  首先我们来读取迷宫数据并标记入口和出口,以及各种障碍物

# 读取maze数据的行列数
row,col = list(map(int,input().split()))
# 存储maze数据的二维数组
maze_data = []
for i in range(row):
    # maze_data.append([s for s in input()])
    In = input()
    maze_data.append([s for s in In])
#starting point
s = (0, 0)
for r in maze_data:
    if 's' in r:
        # 获取起点的行列坐标,从0开始计数
        s = (maze_data.index(r), r.index('s'))

  接下来我们定义走的方向,由于我们只是上下左右移动(四连通),其实就是行列坐标的加一减一:

# 行走方向,四连通方式
direction = [[-1, 0], [0, -1], [1, 0], [0, 1]]

  还有走的时候不应该超过maze的范围,即超过范围的行列坐标pass

# check valid coordinates
def check(x,y):
		nonlocal row,col
		return 0 <= x < row and 0 <= y < col

  这里nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,且只能用于嵌套函数中,因为我们是在一个函数里定义的check函数,所以适用。一般这么用主要是为了避免使用global这种比较不安全和暴力的全局变量声明方式。

  接下来我们来看看一些其他变量的定义:

# 初始化最远路径长度
distance = 100000
# 初始化标记数组,用来避免重复进入节点
max_r_c = max(row, col)
vis = [[0 for _ in range(max_r_c)] for _ in range(max_r_c)]
# 可行通路数量counter
num_paths = 0

  vis数组这里的作用类似于给一个节点能不能走打标签,当我们进入节点A,之后进入节点B,那么我们要将vis里对应A的位置标记,否则再遍历B的可行方向时又会回到A,A又会进入B,导致循环bug

  而当我们退出一个节点C的所有子状态时,应该取消vis里对应的C的标记,因为这仅仅表示这条通路经过C无法通走,不表示其他节点不能通过C组成通路。

  有了上面的准备,我们可以来进行DFS核心函数的编写:

def dfs(x, y, steps):
    """
    maze core function
    Args:
    x (int): row index start from 0
    y (int): col index start from 0
    steps (int): current steps from starting point
    """
    nonlocal vis, distanc
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值