我前几天已经介绍过深度优先搜索算法(DFS),见以下链接:
https://blog.csdn.net/lbh73/article/details/147235900?spm=1011.2415.3001.5331

现在介绍另一种:广度优先搜索算法(BFS)。
在大厂笔试中,这俩种关于图的搜索算法几乎是必考题,也是数据结构 课程中的重要内容。

(文章末尾可扫码加V)

广度优先搜索是一种基于队列数据结构的搜索算法。它从起始节点开始,依次访问其所有相邻节点,然后再依次访问这些相邻节点的相邻节点,如此逐层向外扩展,就像以起始节点为中心,一层一层地向外扩散搜索。

使用队列来实现广度优先搜索。将起始节点加入队列,然后不断从队列头部取出节点进行处理(队列的特点:先进先出),并将其未访问的相邻节点加入队列尾部。这样就保证了先访问的节点的相邻节点先被处理,从而实现逐层搜索。

以下是关于BFS算法的应用。

迷宫路径问题
题目描述
给定一个迷宫,迷宫由 0 和 1 组成,其中 0 表示可以通行,1 表示墙壁。从起点 (0, 0) 到终点 (n-1, n-1) 的最短路径长度是多少?

  1. 问题分析:这是一个典型的最短路径问题,可以使用广度优先搜索(BFS)来解决。
  2. 解题步骤:
    • 使用队列存储当前访问的节点。
    • 每次从队列中取出一个节点,探索其四个方向(上、下、左、右)。
    • 如果到达终点,返回当前步数。

!)

from collections import deque
'''作用:导入双端队列(deque),用于实现高效的队列操作。
deque的popleft()操作时间复杂度为O(1),远优于列表的O(n),适合BFS的先进先出特性。
'''

DIRECTIONS = [(-1, 0), (1, 0), (0, -1), (0, 1)]  # 上下左右移动

START = (0, 0)
#END = (4, 4) 
END = (5, 5)#如果维度有变化,需要修改此地方,例如如果思维为4,那么就修改为:END = (4, 4)

# 可配置参数区(迷宫参数如有变动,用户需修改此区域)
'''
MAZE = [
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 1, 0, 0]
]
'''

MAZE = [
    [0, 1, 0, 0, 0, 1],
    [0, 1, 0, 1, 0, 1],
    [0, 1, 0, 1, 0, 1],
    [0, 0, 0, 1, 0, 1],
    [0, 1, 1, 0, 0, 1],
    [0, 1, 0, 0, 0, 0]
]

'''
作用:定义迷宫(0为通路,1为障碍)、起点、终点和移动方向(上下左右)。
DIRECTIONS表示四个可能的移动方向,与BFS中探索相邻节点的逻辑一致。
'''

def validate_maze(maze, start, end): # 检查坐标越界和起点/终点是否可达
    """验证迷宫合法性"""
    if not maze or not maze[0]:
        raise ValueError("迷宫不能为空")
    if start[0] < 0 or start[1] < 0 or end[0] >= len(maze) or end[1] >= len(maze[0]):
        raise ValueError("坐标越界")
    if maze[start[0]][start[1]] != 0 or maze[end[0]][end[1]] != 0:
        raise ValueError("起点/终点不可达")
'''
作用:验证迷宫的合法性:
迷宫非空且至少有一行。
起点和终点的坐标不越界。
起点和终点必须为通路(值为0)。
边界条件检查逻辑
'''


def bfs_shortest_path(maze, start, end):
    """
    使用BFS算法计算最短路径
    :param maze: 二维迷宫数组
    :param start: 起始坐标元组
    :param end: 终点坐标元组
    :return: 最短路径长度(不可达返回-1)
    """
    validate_maze(maze, start, end) # 合法性检查

    rows, cols = len(maze), len(maze[0])
    visited = [[False] * cols for _ in range(rows)] # 访问标记矩阵
    queue = deque([(start[0], start[1], 0)]) # 初始化队列(坐标+步数)
    visited[start[0]][start[1]] = True
    """	
    作用:
    validate_maze确保输入合法。
    visited矩阵记录节点是否已访问,避免重复探索。
    队列初始化为起点坐标和初始步数0,符合BFS的层级扩展特性。
    """


    while queue:
        x, y, steps = queue.popleft()# 取出队首节点

        if (x, y) == end:
            return steps # 找到终点,返回步数

        for dx, dy in DIRECTIONS: # 遍历四个方向
            nx, ny = x + dx, y + dy
            if 0 <= nx < rows and 0 <= ny < cols: # 检查边界
                if not visited[nx][ny] and maze[nx][ny] == 0:
                    visited[nx][ny] = True
                    queue.append((nx, ny, steps + 1)) # 新节点入队

    return -1 # 队列为空未找到路径

'''
作用:
逐层探索:按层级取出节点,确保首次到达终点的路径是最短的(BFS特性)。
方向遍历:对上下左右四个方向生成新坐标,过滤越界位置。
通路检查:仅当相邻节点未被访问且为通路时,将其加入队列并标记为已访问。
步数记录:每次扩展新节点时步数+1,最终返回的步数即为最短路径长度。
'''


if __name__ == "__main__":  # 根据结果输出路径长度或不可达提示
    try:
        result = bfs_shortest_path(MAZE, START, END)
        if result != -1:
            print(f"最短路径长度是{result}")
        else:
            print("没有可达路径")
    except ValueError as e:
        print(f"输入错误:{str(e)}")
'''
作用:
执行BFS算法并捕获可能的输入错误(如非法迷宫配置)。
结果处理:若有解则输出步数,否则提示无可达路径。
'''

'''
关键设计点
队列选择:使用deque而非列表,提升 popleft() 效率。
步数跟踪:队列元素包含步数信息(参考变量steps),避免额外存储路径节点。
层级扩展:通过步数递增实现逐层探索,确保最短路径优先找到。
合法性检查:在算法开始前验证输入,提高鲁棒性。
'''

运行结果截图:
在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐