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

前言

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

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

  类似如下的输入形式:

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

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

CSDN (゜-゜)つロ 干杯

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
### DFSBFS算法迷宫问题中的应用 #### 一、DFS(深度优先搜索) DFS是一种深入探索的方法,在处理迷宫问题时,该方法会选择一条路径尽可能地下去直到无法继续为止。如果遇到死胡同,则会回退到上一步并尝试其他可能的方向。这种方法可能会找到到达终点的一条路径,但这不一定是最短路径。 ```python def dfs(maze, start, end): stack = [start] visited = set() while stack: pos = stack.pop() if pos == end: return True if pos not in visited: visited.add(pos) for next_pos in get_adjacent_positions(maze, pos): if next_pos not in visited and maze[next_pos[0]][next_pos[1]] != '#': stack.append(next_pos) return False ``` 此代码片段展示了如何利用栈来追踪访问过的节点,并通过递归来遍历迷宫中的每一个分支[^1]。 #### 二、BFS(广度优先搜索) 相比之下,BFS采用逐层扩展的方式来进行搜索。对于迷宫来说,这意味着它将从起点开始逐步向外扩散,每次只前进一小步,从而确保最先发现的目标位置即为最短距离上的目标位置。因此,当涉及到寻找两点间最优路线时,通常推荐使用BFS而非DFS。 ```python from collections import deque def bfs(maze, start, end): queue = deque([start]) visited = {start: None} while queue: current = queue.popleft() if current == end: break for neighbor in get_adjacent_positions(maze, current): if neighbor not in visited and maze[neighbor[0]][neighbor[1]] != '#': visited[neighbor] = current queue.append(neighbor) path = [] while end is not None: path.append(end) end = visited[end] return list(reversed(path)) ``` 上述Python函数实现了基于队列的数据结构执行标准的BFS过程,能够有效地定位从起始点至目的地之间的最佳路径[^2]。 #### 三、两者的主要区别 - **搜索模式**: DFS倾向于沿着单一路线持续前行直至尽头;而BFS则更像水波一样一圈圈地向四周蔓延。 - **适用场景**: 如果只需要知道是否存在通路而不关心具体长度的话可以选择效率较高的DFS;但如果要获取确切的距离信息或最小化行成本,则应考虑使用BFS。 - **内存消耗**: 因为需要保存每一层的状态,所以随着层数增加,BFS占用的空间也会相应增大;相反,由于其特性使得DFS可以在较早阶段就完成任务,故一般情况下所需的额外存储较少[^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值