聊聊广度优先搜索~~~

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

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

拿力扣上的 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,从多个起点同时开始扩散。

相关推荐
西猫雷婶43 分钟前
python学智能算法(二十六)|SVM-拉格朗日函数构造
人工智能·python·算法·机器学习·支持向量机
jstart千语2 小时前
【力扣】第42题:接雨水
算法·leetcode·职场和发展
墨染点香2 小时前
LeetCode 刷题【10. 正则表达式匹配】
算法·leetcode·职场和发展
cookqq2 小时前
mongodb源代码分析createCollection命令由create.idl变成create_gen.cpp过程
数据库·算法·mongodb·nosql
clock的时钟2 小时前
数据结构-线性表顺序表示
数据结构·算法
java1234_小锋3 小时前
【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - snowNLP库实现中文情感分析
python·自然语言处理·flask
**梯度已爆炸**3 小时前
Python Web框架详解:Flask、Streamlit、FastAPI
python·flask·fastapi·streamlit
WanderInk3 小时前
深入解析:Java Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); 一行代码的背后功力
java·后端·算法
এ᭄画画的北北3 小时前
力扣-70.爬楼梯
算法·leetcode
nako_sayuri3 小时前
二分查找:区间内查询数字的频率
数据结构·算法