聊聊广度优先搜索~~~

其实广度优先搜索这东西,说复杂也复杂,说简单也简单。你可以把它想象成小时候玩迷宫,从入口开始,先把周围能一步走到的地方都看一遍,记下来;然后再从这些地方出发,看下一步能到哪些新地方,就这么一层一层往外扩,直到找到出口。这种一层一层 "扫荡" 的思路,就是广度优先搜索最核心的东西。

在代码里,这种思路通常用队列来实现。因为队列的特性是 "先进先出",正好能保证我们先处理完当前层的所有节点,再去处理下一层的。就像排队打饭,先来的先打,打完了后面的再上,不会乱了顺序。

拿力扣上的 102 题 "二叉树的层序遍历" 来说,这题简直是为展示 BFS 量身定做的。题目要求我们按照层级顺序返回二叉树的节点值,比如根节点在第一层,根的左右孩子在第二层,以此类推。

咱们来看看带注释的代码:

ini 复制代码
def levelOrder(root):
    # 如果根节点为空,直接返回空列表
    if not root:
        return []
    # 用于存储最终结果的列表
    result = []
    # 初始化队列,将根节点加入队列
    queue = [root]
    # 当队列不为空时,持续处理
    while queue:
        # 记录当前层的节点数量
        level_size = len(queue)
        # 用于存储当前层节点值的列表
        current_level = []
        # 循环处理当前层的所有节点
        for _ in range(level_size):
            # 从队列头部取出一个节点(队列先进先出)
            node = queue.pop(0)
            # 将当前节点的值加入当前层的列表
            current_level.append(node.val)
            # 如果当前节点有左孩子,将其加入队列(下一层节点)
            if node.left:
                queue.append(node.left)
            # 如果当前节点有右孩子,将其加入队列(下一层节点)
            if node.right:
                queue.append(node.right)
        # 将当前层的结果加入最终结果列表
        result.append(current_level)
    # 返回最终的层序遍历结果
    return result

再看一道稍微复杂点的,力扣 200 题 "岛屿数量"。这题是说给你一个由 '1'(陆地)和 '0'(水)组成的二维网格,让你计算岛屿的数量。岛屿是由相邻的陆地连接形成的,而且四周都是水,每个格子只和上下左右四个方向相邻。

带注释的代码如下:

ini 复制代码
def numIslands(grid):
    # 如果网格为空或者网格的第一行为空,返回0
    if not grid or not grid[0]:
        return 0
    # 获取网格的行数和列数
    rows, cols = len(grid), len(grid[0])
    # 用于记录岛屿数量
    count = 0
    # 遍历网格中的每个单元格
    for i in range(rows):
        for j in range(cols):
            # 当遇到陆地('1')时
            if grid[i][j] == '1':
                # 岛屿数量加1
                count += 1
                # 初始化队列,将当前陆地坐标加入队列
                queue = [(i, j)]
                # 将当前陆地标记为已访问(改为'0'),避免重复计算
                grid[i][j] = '0'
                # 当队列不为空时,继续处理
                while queue:
                    # 从队列头部取出一个坐标
                    x, y = queue.pop(0)
                    # 定义上下左右四个方向的偏移量
                    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                        # 计算相邻单元格的坐标
                        nx, ny = x + dx, y + dy
                        # 检查相邻单元格是否在网格范围内,且是未访问的陆地
                        if 0 <= nx < rows and 0 <= ny < cols and grid[nx][ny] == '1':
                            # 将相邻陆地标记为已访问
                            grid[nx][ny] = '0'
                            # 将相邻陆地坐标加入队列,继续扩散
                            queue.append((nx, ny))
    # 返回岛屿的总数量
    return count

还有力扣 994 题 "腐烂的橘子",这道题是多源 BFS 的典型应用。题目是说,在给定的 m x n 网格中,每个单元格可以是新鲜橘子(1)、腐烂的橘子(2)或空(0)。腐烂的橘子会在每分钟内使相邻四个方向上的新鲜橘子腐烂。求直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 - 1。

带注释的代码如下:

ini 复制代码
def orangesRotting(grid):
    # 获取网格的行数和列数
    rows, cols = len(grid), len(grid[0])
    # 初始化队列,用于存储腐烂橘子的坐标
    queue = []
    # 记录新鲜橘子的数量
    fresh = 0
    # 遍历网格,找出初始的腐烂橘子和新鲜橘子
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == 2:
                # 将腐烂橘子的坐标加入队列
                queue.append((i, j))
            elif grid[i][j] == 1:
                # 统计新鲜橘子数量
                fresh += 1
    # 如果没有新鲜橘子,直接返回0
    if fresh == 0:
        return 0
    # 定义上下左右四个方向的偏移量
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    # 记录经过的分钟数
    minutes = 0
    # 当队列不为空时,继续处理
    while queue:
        # 记录当前队列的长度(即当前分钟要处理的腐烂橘子数量)
        size = len(queue)
        # 标记当前分钟是否有新鲜橘子被腐烂
        has_rotten = False
        # 处理当前分钟的所有腐烂橘子
        for _ in range(size):
            # 从队列头部取出一个腐烂橘子的坐标
            x, y = queue.pop(0)
            # 检查四个方向的相邻单元格
            for dx, dy in directions:
                nx, ny = x + dx, y + dy
                # 检查相邻单元格是否在网格范围内,且是新鲜橘子
                if 0 <= nx < rows and 0 <= ny < cols and grid[nx][ny] == 1:
                    # 将新鲜橘子变为腐烂橘子
                    grid[nx][ny] = 2
                    # 新鲜橘子数量减1
                    fresh -= 1
                    # 将新腐烂的橘子坐标加入队列
                    queue.append((nx, ny))
                    # 标记当前分钟有橘子被腐烂
                    has_rotten = True
        # 如果当前分钟有橘子被腐烂,分钟数加1
        if has_rotten:
            minutes += 1
    # 如果还有新鲜橘子没被腐烂,返回-1,否则返回分钟数
    return -1 if fresh > 0 else minutes

这三道题虽然场景不同,但都围绕着 BFS 的核心思想展开。二叉树的层序遍历是基础的层级遍历,岛屿数量是通过 BFS 来标记连通区域,腐烂的橘子则是多源 BFS,从多个起点同时开始扩散。

相关推荐
YLCHUP4 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
2601_967979125 小时前
Python第七十四讲 异常处理完全精通 try-except零基础保姆级教程
python·异常处理·编程教程·错误处理·try-except
思麟呀5 小时前
背包DP(01背包+完全背包)
c++·算法
wuyk5555 小时前
Spider-第07讲-JSON接口与正则提取
爬虫·python·json
2601_967987376 小时前
Sqlite参考手册
数据库·python·sqlite·api·教程
笑容掩饰我的失落6 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
用户019027581616 小时前
A 股到价提醒机器人:AlphaFeed + 飞书/钉钉 Webhook
python
特立独行的猫A7 小时前
Ionic + 仓颉语言的 cj-tauri 开发框架实战:开发精美移动端 APP
java·后端·python
Pniubi7 小时前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
高洁017 小时前
数据同化与动态状态估计:让孪生持续贴合现实
人工智能·python·深度学习·transformer·迁移学习