多源BFS(典型算法思想)—— OJ例题算法解析思路

news/2025/2/24 12:55:23

目录

一、542. 01 矩阵 - 力扣(LeetCode)

算法代码: 

代码逻辑思路

数据结构初始化

步骤一:队列初始化

步骤二:广度优先搜索

返回结果

关键点总结

广度优先搜索(BFS)

访问标记

复杂度分析

二、1020. 飞地的数量 - 力扣(LeetCode) 

算法代码: 

代码逻辑思路

数据结构初始化

步骤一:将边界上的陆地单元格加入队列

步骤二:多源 BFS 扩展

步骤三:统计被水包围的陆地单元格数量

关键点总结

多源 BFS

访问标记

复杂度分析

三、1765. 地图中的最高点 - 力扣(LeetCode)

算法代码: 

代码逻辑思路

关键点总结

四、1162. 地图分析 - 力扣(LeetCode)

算法代码: 

代码逻辑思路

关键点总结


一、542. 01 矩阵 - 力扣(LeetCode)

算法代码: 

class Solution {
    int dx[4] = {0, 0, 1, -1}; // 四个方向的 x 偏移量
    int dy[4] = {1, -1, 0, 0}; // 四个方向的 y 偏移量

public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        int m = mat.size(), n = mat[0].size();

        // dist[i][j] == -1 表示没有搜索过
        // dist[i][j] != -1 表示最短距离
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 初始化距离矩阵为 -1
        queue<pair<int, int>> q; // BFS 队列

        // 1. 把所有的源点加入到队列中
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (mat[i][j] == 0) { // 找到 0 的位置
                    q.push({i, j}); // 入队
                    dist[i][j] = 0; // 距离为 0
                }

        // 2. 一层一层的往外扩展
        while (q.size()) {
            auto [a, b] = q.front(); // 获取队头元素
            q.pop();
            for (int i = 0; i < 4; i++) { // 遍历四个方向
                int x = a + dx[i], y = b + dy[i]; // 计算新位置
                // 检查新位置是否合法并且未被访问
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {
                    dist[x][y] = dist[a][b] + 1; // 更新距离
                    q.push({x, y}); // 入队
                }
            }
        }
        return dist; // 返回结果矩阵
    }
};

代码逻辑思路

  1. 数据结构初始化

    • 使用 m 和 n 分别表示矩阵的行数和列数。

    • 创建一个 dist 矩阵,用于存储每个单元格到最近零的距离,初始化为 -1,表示尚未被搜索过。

    • 使用一个队列 q 来实现广度优先搜索(BFS)。

  2. 步骤一:队列初始化

    • 遍历整个矩阵 mat,将所有的 0 的位置(源点)加入队列 q,并将对应的 dist 值设为 0,因为从 0 到 0 的距离为 0

  3. 步骤二:广度优先搜索

    • 使用 BFS 从所有的 0 出发,逐层向外扩展。

    • 在每次循环中,取出队列的前一个元素 (a, b),表示当前正在处理的位置。

    • 遍历四个方向(上下左右),计算 (x, y) 的新位置。

    • 检查新位置是否在矩阵范围内,且 dist[x][y] 为 -1(表示尚未访问过):

      • 如果是,则将 dist[x][y] 更新为 dist[a][b] + 1,表示新位置到最近 0 的距离。

      • 把新位置 (x, y) 加入队列 q

  4. 返回结果

    • 当队列为空时,所有位置的最短距离都已经计算完毕,返回 dist 矩阵。

关键点总结

  1. 广度优先搜索(BFS)

    • BFS 是解决最短路径问题的经典方法,适合用于从多个源点扩展到其它点的场景。

    • 通过从所有的 0 开始,同时向外扩展,确保每个位置的最短路径都能被正确计算。

  2. 访问标记

    • 使用 dist 数组来标记每个位置是否已被访问,并存储到最近 0 的距离。

  3. 复杂度分析

    • 时间复杂度为 O(m * n),因为每个位置最多被访问一次。

    • 空间复杂度为 O(m * n),用于存储 dist 矩阵和 BFS 队列。

这种方法有效且高效,适用于计算二维矩阵中元素到最近目标元素的距离问题。

二、1020. 飞地的数量 - 力扣(LeetCode) 

算法代码: 

class Solution {
    int dx[4] = {0, 0, 1, -1}; // 四个方向的 x 偏移量
    int dy[4] = {1, -1, 0, 0}; // 四个方向的 y 偏移量

public:
    int numEnclaves(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<bool>> vis(m, vector<bool>(n)); // 访问标记
        queue<pair<int, int>> q; // BFS 队列
        
        // 1. 把边上的 1 加入到队列中
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    if (grid[i][j] == 1) { // 找到边界上的陆地
                        q.push({i, j}); // 入队
                        vis[i][j] = true; // 标记为已访问
                    }
                }
        
        // 2. 多源 BFS 扩展
        while (q.size()) {
            auto [a, b] = q.front(); // 取出队头元素
            q.pop();
            for (int i = 0; i < 4; i++) { // 遍历四个方向
                int x = a + dx[i], y = b + dy[i]; // 计算新位置
                // 检查新位置是否合法并且是陆地且未被访问
                if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y]) {
                    vis[x][y] = true; // 标记为已访问
                    q.push({x, y}); // 入队
                }
            }
        }
        
        // 3. 统计结果
        int ret = 0; // 统计被水包围的陆地数量
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j] == 1 && !vis[i][j]) // 找到未被访问的陆地
                    ret++; // 增加计数
        
        return ret; // 返回结果
    }
};

代码逻辑思路

  1. 数据结构初始化

    • 使用 m 和 n 分别表示网格的行数和列数。

    • 创建一个 vis 矩阵,用于记录每个单元格是否被访问过。

    • 使用一个队列 q 来进行 BFS。

  2. 步骤一:将边界上的陆地单元格加入队列

    • 遍历整个网格 grid,在边界(第一行、最后一行、第一列、最后一列)中寻找值为 1 的单元格。

    • 如果找到这样的单元格,将它加入队列 q,并标记为已访问。

  3. 步骤二:多源 BFS 扩展

    • 使用 BFS 从队列中的所有陆地单元格开始,逐层向外扩展。

    • 在每次循环中,取出队列的前一个元素 (a, b),表示当前正在处理的位置。

    • 遍历四个方向(上下左右),计算新位置 (x, y)

    • 检查新位置是否在矩阵范围内,且值为 1,并且未被访问:

      • 如果满足条件,标记该位置为已访问,并将其加入队列 q

  4. 步骤三:统计被水包围的陆地单元格数量

    • 遍历整个网格 grid,统计值为 1 并且未被访问的单元格的数量,表示这些单元格是被水包围的陆地单元格。

    • 返回这个计数作为结果。

关键点总结

  1. 多源 BFS

    • 从所有边界的陆地单元格开始,同时向内扩展,确保能够覆盖所有与边界相连的陆地。

    • BFS 确保每个陆地单元格仅被访问一次。

  2. 访问标记

    • 使用 vis 数组来标记每个单元格是否已被访问,避免重复计算。

  3. 复杂度分析

    • 时间复杂度为 O(m * n),因为每个单元格最多被访问一次。

    • 空间复杂度为 O(m * n),用于存储访问标记和 BFS 队列。

这种方法有效且高效,适用于计算被水包围的陆地单元格的数量。

三、1765. 地图中的最高点 - 力扣(LeetCode)

算法代码: 

class Solution {
    int dx[4] = {0, 0, 1, -1}; // 四个方向的 x 偏移量
    int dy[4] = {1, -1, 0, 0}; // 四个方向的 y 偏移量

public:
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {
        int m = isWater.size(), n = isWater[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 初始化高度矩阵为 -1
        queue<pair<int, int>> q; // BFS 队列
        
        // 1. 把所有的源点加入到队列里面
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (isWater[i][j]) { // 找到水域单元格
                    dist[i][j] = 0; // 高度设为 0
                    q.push({i, j}); // 入队
                }
        
        // 2. 多源 BFS 扩展
        while (q.size()) {
            auto [a, b] = q.front(); // 获取队头元素
            q.pop();
            for (int i = 0; i < 4; i++) { // 遍历四个方向
                int x = a + dx[i], y = b + dy[i]; // 计算新位置
                // 检查新位置是否合法并且尚未计算高度
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {
                    dist[x][y] = dist[a][b] + 1; // 更新高度
                    q.push({x, y}); // 入队
                }
            }
        }
        
        return dist; // 返回结果矩阵
    }
};

代码逻辑思路

  1. 数据结构初始化

    • 使用 m 和 n 分别表示矩阵的行数和列数。

    • 创建一个 dist 矩阵,用于存储每个单元格的高度,初始值为 -1,表示尚未计算。

    • 使用一个队列 q 来实现 BFS。

  2. 步骤一:将所有水域单元格加入队列

    • 遍历整个矩阵 isWater,找到所有值为 1 的单元格(水域)。

    • 将这些水域单元格的对应位置在 dist 矩阵中设置为 0,并将其加入队列 q

  3. 步骤二:多源 BFS 扩展

    • 使用 BFS 从队列中的所有水域单元格开始,逐层向外扩展。

    • 在每次循环中,取出队列的前一个元素 (a, b),表示当前正在处理的位置。

    • 遍历四个方向(上下左右),计算新位置 (x, y)

    • 检查新位置是否在矩阵范围内,且 dist[x][y] 为 -1(表示尚未计算):

      • 如果满足条件,则更新 dist[x][y] 为 dist[a][b] + 1,表示新位置到最近水域的距离。

      • 将新位置 (x, y) 加入队列 q

  4. 返回结果

    • 当队列为空时,所有位置的高度都已经计算完毕,返回 dist 矩阵。

关键点总结

  1. 多源 BFS

    • BFS 从所有水域单元格同时开始,同时向内扩展,确保所有陆地单元格的高度能够被正确计算。

    • BFS 能够保证每个陆地单元格的高度是距离最近水域单元格的最短距离。

  2. 访问标记

    • 使用 dist 数组来标记每个单元格的高度,并避免重复计算。

  3. 复杂度分析

    • 时间复杂度为 O(m * n),每个单元格最多被访问一次。

    • 空间复杂度为 O(m * n),用于存储高度矩阵和 BFS 队列。

        这种方法有效且高效,适用于计算二维矩阵中每个单元格的高度,特别是在有障碍物(如水域)时。

四、1162. 地图分析 - 力扣(LeetCode)

算法代码: 

class Solution {
    int dx[4] = {0, 0, 1, -1}; // 四个方向的 x 偏移量
    int dy[4] = {1, -1, 0, 0}; // 四个方向的 y 偏移量

public:
    int maxDistance(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 初始化距离矩阵为 -1
        queue<pair<int, int>> q; // BFS 队列
        
        // 1. 把所有陆地单元格加入到队列里面
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                if (grid[i][j]) { // 判断是否为陆地
                    dist[i][j] = 0; // 陆地的距离为 0
                    q.push({i, j}); // 入队
                }

        int ret = -1; // 最大距离初始化为 -1
        // 2. 多源 BFS 扩展
        while (q.size()) {
            auto [a, b] = q.front(); // 获取队头元素
            q.pop();
            for (int i = 0; i < 4; i++) { // 遍历四个方向
                int x = a + dx[i], y = b + dy[i]; // 计算新位置
                // 检查新位置是否合法且尚未计算
                if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {
                    dist[x][y] = dist[a][b] + 1; // 更新距离
                    q.push({x, y}); // 入队
                    ret = max(ret, dist[x][y]); // 更新最大距离
                }
            }
        }
        return ret; // 返回最大距离
    }
};

代码逻辑思路

  1. 数据结构初始化

    • 使用 m 和 n 分别表示网格的行数和列数。

    • 创建一个 dist 矩阵,用于存储每个单元格的距离,初始值为 -1,表示尚未计算。

    • 使用一个队列 q 来实现 BFS。

  2. 步骤一:将所有陆地单元格加入队列

    • 遍历整个网格 grid,找到所有值为 1 的单元格(陆地)。

    • 将这些陆地单元格的对应位置在 dist 矩阵中设置为 0,并将它们加入队列 q

  3. 步骤二:多源 BFS 扩展

    • 使用 BFS 从队列中的所有陆地单元格开始,逐层向外扩展。

    • 在每次循环中,取出队列的前一个元素 (a, b),表示当前正在处理的位置。

    • 遍历四个方向(上下左右),计算新位置 (x, y)

    • 检查新位置是否在矩阵范围内,且 dist[x][y] 为 -1(表示尚未计算):

      • 如果满足条件,则更新 dist[x][y] 为 dist[a][b] + 1,表示新位置到最近水域的距离。

      • 将新位置 (x, y) 加入队列 q

      • 在更新后,更新 ret 为当前最大距离。

  4. 返回结果

    • 当队列为空时,所有位置的最大距离都已经计算完毕,返回 ret

关键点总结

  1. 多源 BFS

    • 从所有陆地单元格同时开始,BFS 确保所有水域单元格的最大距离能够被正确计算。

    • BFS 的逐层扩展特性确保了每个单元格到最近水域单元格的距离是最短的。

  2. 访问标记

    • 使用 dist 数组来标记每个单元格的距离,并避免重复计算。

  3. 复杂度分析

    • 时间复杂度为 O(m * n),每个单元格最多被访问一次。

    • 空间复杂度为 O(m * n),用于存储距离矩阵和 BFS 队列。

这种方法有效且高效,适用于计算二维矩阵中每个陆地单元格到最近水域单元格的最大距离。


http://www.niftyadmin.cn/n/5864340.html

相关文章

W803|联盛德|WM IoT SDK2.X测试|(1)开箱:开发板及说明

前几天关注的联盛德微电子新推出了WM IoT SDK2.X&#xff0c;正式发布后&#xff0c;邀请用户参加“免费试用&#xff0c;赢千元大礼”活动&#xff0c;填写信息&#xff0c;等待统一发送&#xff0c;很快收到了板子。 活动地址&#xff1a;联盛德微电子WM IoT SDK2.X正式发布…

java实现二维码图片生成和编解码

java实现二维码图片生成和编解码 在wutool中&#xff0c;封装了二维码工具类&#xff0c;基于google的zxing库&#xff0c;实现二维码图片生成、编码和解码。 关于wutool wutool是一个java代码片段收集库&#xff0c;针对特定场景提供轻量解决方案&#xff0c;只要按需选择代…

React Native 0.76正式版发布,带来多项目更新

去年10月, React Native 0.76版本更新,主要更新的内容有默认启用了新架构,并引入了 React Native DevTools。这是我们团队 6 年辛勤工作的高潮,同时得到了我们令人难以置信的开发者社区的支持。主要更新的内容如下: React Native 新架构默认启用 React Native DevTools 更…

Python 数据分析概述 ①

一文读懂Python数据分析&#xff1a;从基础到实践全攻略 在当今数字化浪潮中&#xff0c;数据分析已然成为解锁海量数据价值的关键钥匙&#xff0c;而Python凭借其独特优势&#xff0c;在数据分析领域大放异彩。今天&#xff0c;咱们就结合教学PPT内容&#xff0c;深入探索Pyt…

10. 九转金丹炼矩阵 - 矩阵置零(标记优化)

哪吒在数据修仙界中继续他的修炼之旅。这一次,他来到了一片神秘的金丹谷,谷中有一座巨大的九转金丹炉,炉身闪烁着神秘的光芒。金丹炉的入口处有一块巨大的石碑,上面刻着一行文字:“欲破此炉,需以九转金丹之力,炼矩阵之零,标记优化定乾坤。” 哪吒定睛一看,石碑上还有…

QT中经常出现的用法:组合

在 C 中&#xff0c;一个类包含另一个类的对象称为组合&#xff08; Composition &#xff09;。这是一种常见的设计模式&#xff0c;用 于表示一个类是由另一个类的对象组成的。这种关系通常表示一种 " 拥有 " &#xff08; "has-a" &#xff09;的关系。…

STM32——HAL库开发笔记22(定时器3—呼吸灯实验)(参考来源:b站铁头山羊)

本文利用前几节所学知识来实现一个呼吸灯实验&#xff1a;两颗led灯交替呼吸。 一、STM32CubeMX配置 step1&#xff1a;配置调试接口 step2&#xff1a;配置定时器 定时器1位于APB2总线上&#xff0c;如上图所示。 step3&#xff1a;配置时基单元 按照下图配置 时钟来源配置…

网络运维学习笔记 022 HCIA-Datacom新增知识点03园区网典型组网架构及案例实战

园区网典型组网架构及案例实战 园区网&#xff1a;内部运行了园区网协议的一个主体网络 园区网络典型架构 园区网络常用协议与技术&#xff1a; 接入层&#xff1a; VLAN、生成树、链路聚合、AAA、dhcp-snooping等 汇聚层&#xff1a;DHCP、堆叠、链路聚合、生成树、OSPF、静…