如何在网格中模拟腐烂扩散:如何使用广度优先搜索(BFS)解题

news2024/10/3 18:53:27

问题描述

你需要在一个二维的网格中处理橘子的腐烂扩散过程,网格中的每个单元格可以有三种状态:

  • 0:表示空格,没有橘子。
  • 1:表示一个新鲜的橘子。
  • 2:表示一个腐烂的橘子,它可以在 1 分钟内让上下左右四个方向的新鲜橘子也变腐烂。

你的任务是:计算让所有橘子腐烂所需的最少时间。如果存在无法腐烂的橘子,返回 -1

994. 腐烂的橘子 - 力扣(LeetCode)

这个问题可以看作一个 广度优先搜索(BFS) 问题,类似于在网格中扩散的过程。每个腐烂的橘子会以每分钟的时间向四周扩散,找到所有橘子完全腐烂所需要的最短时间。如果存在无法腐烂的橘子,则返回 -1

我们可以借助 BFS 的特性,一步步地扩散腐烂橘子,并记录时间。

如何理解最短腐烂时间?

最短腐烂时间的概念类似于在广度优先搜索中寻找从一个起点扩展到所有节点的最短路径。换句话说,每个腐烂的橘子以相同的速度向四周扩散,我们要找到让所有新鲜橘子完全腐烂所需的最少分钟数。

BFS 的优势
  • 层次遍历:BFS 是按层次遍历的,也就是每次扩散到下一层需要的步数(分钟数)是相同的。
  • 最短路径保证:BFS 会确保我们从某个腐烂橘子第一次到达某个新鲜橘子的时间最短。因为每次扩展的步数都是均匀的,先腐烂的橘子会先腐蚀周围的新鲜橘子。

解题思路:

  1. 数据结构选择

    • 使用队列(queue)来进行 BFS,因为我们需要逐层处理腐烂橘子的扩散。
    • 每次从队列中取出一个腐烂橘子,并向它的上下左右四个方向扩展。如果碰到新鲜橘子,则将其变为腐烂橘子,并加入队列。
  2. 初始化队列

    • 首先遍历整个网格,将所有腐烂的橘子(值为 2)的坐标加入队列,作为 BFS 的起点。
    • 同时统计新鲜橘子(值为 1)的数量。如果一开始没有新鲜橘子,则直接返回 0
  3. BFS 扩展

    • 从队列中取出腐烂橘子,向四个方向扩展,感染新鲜橘子。每次感染新鲜橘子,腐烂时间加 1,并将新鲜橘子加入队列。
    • 记录经过的时间,直到没有新鲜橘子可以腐烂。
  4. 处理无法腐烂的情况

    • 如果 BFS 结束时,仍然有新鲜橘子(即队列处理完毕但仍有 1 存在),返回 -1
    • 如果所有新鲜橘子都腐烂了,返回腐烂所需要的最短时间。
  5. 处理 -1 的特殊情况

    • 如果有橘子永远无法被腐烂,例如它们周围被 0 包围,最终会返回 -1

代码实现

#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        
        // 队列用于存放腐烂橘子的坐标
        queue<pair<int, int>> q;
        int fresh_count = 0;  // 新鲜橘子的数量
        
        // 遍历整个网格,初始化队列并统计新鲜橘子的数量
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 2) {
                    q.push({i, j});  // 将腐烂的橘子放入队列
                } else if (grid[i][j] == 1) {
                    fresh_count++;  // 统计新鲜橘子的数量
                }
            }
        }
        
        // 如果一开始就没有新鲜橘子,直接返回 0
        if (fresh_count == 0) return 0;
        
        // 方向数组,用于向四个方向扩展:上、下、左、右
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        
        int minutes = 0;  // 记录经过的分钟数
        
        // 开始 BFS
        while (!q.empty()) {
            int size = q.size();  // 当前层的腐烂橘子数量
            bool has_rotted = false;  // 记录是否有新橘子腐烂
            
            for (int i = 0; i < size; i++) {
                auto [x, y] = q.front();
                q.pop();
                
                // 遍历四个方向
                for (auto [dx, dy] : directions) {
                    int newX = x + dx;
                    int newY = y + dy;
                    
                    // 检查新的坐标是否有效并且是新鲜橘子
                    if (newX >= 0 && newX < m && newY >= 0 && newY < n && grid[newX][newY] == 1) {
                        // 新鲜橘子腐烂
                        grid[newX][newY] = 2;
                        fresh_count--;  // 新鲜橘子数量减少
                        q.push({newX, newY});  // 将新腐烂的橘子加入队列
                        has_rotted = true;  // 标记当前层有橘子腐烂
                    }
                }
            }
            
            // 只有当当前层有新鲜橘子腐烂时,才增加时间
            if (has_rotted) minutes++;
        }
        
        // 如果最终还有新鲜橘子没有腐烂,返回 -1
        return fresh_count == 0 ? minutes : -1;
    }
};

代码逻辑详细解释

  1. 初始化队列与统计新鲜橘子

    • 遍历整个网格,遇到腐烂橘子(值为 2)时,将其坐标存入队列。
    • 同时统计新鲜橘子(值为 1)的数量。如果没有新鲜橘子,直接返回 0,因为不需要腐烂操作。
  2. BFS 扩展腐烂过程

    • 队列中每个元素表示一个腐烂的橘子。我们从队列中取出腐烂橘子,检查其四个方向(上、下、左、右)是否有新鲜橘子。
    • 如果找到新鲜橘子,就将其标记为腐烂并加入队列。同时将新鲜橘子的数量减少。
  3. 时间记录

    • 每处理完当前层的腐烂橘子,若有新鲜橘子被腐烂,才将时间 minutes 增加。
    • 如果在某一层的所有腐烂橘子都不能腐烂周围的橘子,那么本层结束后不增加时间。
  4. 处理无法腐烂的橘子

    • BFS 结束后,若还有新鲜橘子(fresh_count > 0),说明有橘子无法被腐烂。此时返回 -1
  5. 返回结果

    • 如果所有橘子都腐烂了,则返回腐烂的总时间。否则返回 -1

示例讲解

示例 1:
grid = [[2,1,1],
        [1,1,0],
        [0,1,1]]

在这里插入图片描述

过程

  • 初始腐烂橘子坐标为 (0,0)

  • 第 1 分钟:腐烂橘子 (0,0) 腐烂周围的橘子 (0,1)(1,0)

    • 队列状态:[(0,1), (1,0)]
    • 剩余新鲜橘子数量:4
  • 第 2 分钟:橘子 (0,1)(1,0) 分别腐烂其相邻的橘子 (0,2)(1,1)

    • 队列状态:[(0,2), (1,1)]
    • 剩余新鲜橘子数量:2
  • 第 3 分钟:橘子 (0,2)(1,1) 分别腐烂其相邻的橘子 (1,2)(2,1)

    • 队列状态:[(1,2), (2,1)]
    • 剩余新鲜橘子数量:1
  • 第 4 分钟:橘子 (1,2)(2,1) 腐烂最后一个新鲜橘子 (2,2)

    • 队列状态:[(2,2)]
    • 剩余新鲜橘子数量:0
  • 所有橘子腐烂,总共耗时 4 分钟。

输出:4

示例 2:
grid = [[2,1,1],
        [0,1,1],
        [1,0,1]]

过程

  • 第一轮 BFS
    • 从队列中取出 (0,0),它周围的四个方向分别是:
      1. (1,0),这是 0,无法通过墙传播。
      2. (-1,0),超出边界。
      3. (0,1),这是新鲜橘子 1,变为腐烂橘子,并加入队列。
      4. (0,-1),超出边界。

队列状态:[(0,1)],剩余新鲜橘子:5,时间增加 minutes++(时间变为 1

  • 第二轮 BFS
    • 从队列中取出 (0,1),它周围的四个方向分别是:
      1. (1,1),新鲜橘子,变为腐烂,加入队列。
      2. (-1,1),超出边界。
      3. (0,2),新鲜橘子,变为腐烂,加入队列。
      4. (0,0),已经是腐烂橘子,跳过。

队列状态:[(1,1), (0,2)],剩余新鲜橘子:3,时间增加 minutes++(时间变为 2

  • 第三轮 BFS
    • 从队列中取出 (1,1)(0,2),它们周围分别是:
      1. (2,1),是 0,不能腐烂。
      2. (1,2),新鲜橘子,变为腐烂,加入队列。
      3. (1,0),是 0,无法腐烂。

队列状态:[(1,2)],剩余新鲜橘子:2,时间增加 minutes++(时间变为 3

  • 第四轮 BFS
    • 从队列中取出 (1,2),它周围的四个方向分别是:
      1. (2,2),新鲜橘子,变为腐烂,加入队列。
      2. (0,2),已经腐烂。
      3. (1,1),已经腐烂。
      4. (1,3),超出边界。

队列状态:[(2,2)],剩余新鲜橘子:1,时间增加 minutes++(时间变为 4

  1. 取出 [(2,2)]

    • (2,2) 是最后一个腐烂橘子。

    • 它的四个方向的检查:

      1. (1,2) 已经腐烂,跳过。
      2. (2,1) 是墙壁 0,无法腐烂。
      3. (3,2) 超出边界。
      4. (2,3) 超出边界。
    • 这时候队列 q 变为空。

  2. 跳出 BFS 循环

    • 由于队列为空,while (!q.empty()) 条件不再成立,循环结束。
  3. 判断是否有剩余新鲜橘子

    • 循环结束后,代码会检查是否仍有未腐烂的新鲜橘子。
    • 此时,grid[2][0] 橘子由于被 grid[1][0] 的墙壁阻隔,它从未进入过 BFS 队列,也没有腐烂。
    • 因此,freshOranges1,仍然大于 0,即还有剩余的新鲜橘子,无法被腐烂。
    • 代码中执行:
    return freshOranges == 0 ? minutes : -1;
    
    • 由于 freshOranges > 0,返回 -1
示例 3:
grid = [[0,2]]
  • 网格中一开始没有新鲜橘子,直接返回 0

输出:0

总结

  1. BFS 思路:通过队列进行层次遍历,模拟腐烂橘子的扩散过程。
  2. 特殊情况处理:如果一开始没有新鲜橘子,直接返回 0;如果最后还有新鲜橘子没有腐烂,返回 -1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2186708.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模拟算法(1)_替换所有的问号

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(1)_替换所有的问号 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. …

MHA携手Atlas:打造高效读写分离解决方案,引领数据库性能飞跃

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

npm切换到淘宝镜像

1、输入以下命令后回车&#xff0c;npm切换至淘宝镜像 npm config set registry https://registry.npmmirror.com 2、输入以下命令后回车&#xff0c;检查是否切换成功 npm config get registry 若返回此信息&#xff0c;表示切换成功 3、切换后就可使用淘宝镜像加快npm包的…

C语言 | Leetcode C语言题解之第447题回旋镖的数量

题目&#xff1a; 题解&#xff1a; int cmpfunc(const void *a, const void *b) {return (*(int *)a - *(int *)b); } //计算组合数*2 int every(int count) {if (count 1) {return 0;} else {return count * (count - 1);} } //计算每个锚点能产生的回旋镖总数 int part(in…

【嵌入式系统】第18章 脉宽调试器(PWM)

目录 18.1 结构框图 18.3 功能说明 18.3.4 PWM 信号发生器 18.3.5 死区发生器 18.3.6 中断/ADC 触发选择器 18.3.7 同步方法 18.3.8 故障条件 18.3.9 输出控制块 LES 硬件介绍&#xff08;12&#xff09;正交编码接口QEI 19.1 结构框图 19.2 信号描述 19.3 功能说明…

4M-21: An Any-to-Any Vision Model for Tens of Tasks and Modalities论文精度

贡献&#xff1a;在21种高度不同的模态中训练一个统一的模型&#xff0c;并且对比专有模型不会有性能损失做法&#xff1a;将不同模态映射到不同的token空间&#xff0c;并且可以生成不同的模态token【Any-to-any】关键点&#xff1a;如何在不同的模态中应用tokenization进行映…

【MySQL 07】内置函数

目录 1.日期函数 日期函数使用场景&#xff1a; 2.字符串函数 字符串函数使用场景&#xff1a; 3.数学函数 4.控制流函数 1.日期函数 函数示例&#xff1a; 1.在日期的基础上加日期 在该日期下&#xff0c;加上10天。 2.在日期的基础上减去时间 在该日期下减去2天 3.计算两…

【MySQL】服务器管理与配置

MySQL服务器 服务器默认配置 查看服务器默认选项和系统变量 mysqld --verbose --help 查看运行时的系统变量&#xff0c;可以通过like去指定自己要查询的内容 状态变量的查看 系统变量和状态变量的作用域 全局作用域&#xff1a; 对于每个会话都会生效当前会话&#xff1a;只…

通信工程学习:什么是SNMP简单网络管理协议

SNMP&#xff1a;简单网络管理协议 SNMP&#xff08;Simple Network Management Protocol&#xff0c;简单网络管理协议&#xff09;是一种用于在计算机网络中管理网络节点&#xff08;如服务器、工作站、路由器、交换机等&#xff09;的标准协议。它属于OSI模型的应用层&#…

TIM的PWM模式

定时器的工作流程: 定时器对时钟传来的脉冲次数计数&#xff0c;并且在次数到达范围值时触发中断。如向下计数模式时为0&#xff0c;向上计数为达到自动重装载计时器的值时触发中断。 STM32里面的定时器有多个定时器。 如TIM1、TIM2、TIM3 定时器的输入捕获模式来测量输…

我为什么决定关闭ChatGPT的记忆功能?

你好&#xff0c;我是三桥君 几个月前&#xff0c;ChatGPT宣布即将推出一项名为“记忆功能”的新特性&#xff0c;英文名叫memory。 这个功能听起来相当吸引人&#xff0c;宣传口号是让GPT更加了解用户&#xff0c;仿佛是要为我们每个人量身打造一个专属的AI助手。 在记忆功…

【笔记】平面

一、平面及其方程&#xff08;3个条件&#xff0c;4种表达&#xff09; F ( x , y , z ) F(x,y,z) F(x,y,z)为平面方程&#xff1a; 在这个平面上的点满足 F ( x , y , z ) 0 F(x,y,z)0 F(x,y,z)0不在这个平面上的点不满足 F ( x , y , z ) 0 F(x,y,z)0 F(x,y,z)0 归根结底&…

Python 课程23-LibROSA

前言 LibROSA 是一个用于音频分析的 Python 库&#xff0c;特别擅长音乐信号处理和音频特征提取。它提供了广泛的工具来处理音频文件&#xff0c;包括加载、变换、特征提取、可视化等功能。LibROSA 在音乐信息检索&#xff08;MIR&#xff09;、机器学习中的音频预处理和音频信…

java发送邮件email实战

1.首先在项目中增加依赖&#xff0c;在pom文件中添加如下坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>2.发邮件工具类如下 package com.example.demo.…

安卓13默认使用大鼠标 与配置分析 andriod13默认使用大鼠标 与配置分析

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 android13里面的鼠标貌似比以前版本的鼠标小了,有些客户想要把这个鼠标改大。这个功能,android有现成的,就在这里,设置 =》无障碍 =》色彩和动画 =》 大号鼠标指针。 我们通过…

JavaWeb - 8 - 请求响应 分层解耦

请求响应 请求&#xff08;HttpServletRequest&#xff09;&#xff1a;获取请求数据 响应&#xff08;HttpServletResponse&#xff09;&#xff1a;设置响应数据 BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程…

【解决方案】关于 UART 接收数据时丢失数据的解决办法——环形缓冲存储区

文章目录 UART 通信丢失数据的常见原因总结串口&#xff08;UART&#xff09;数据丢失 Bug 的复现引入环形队列解决数据丢失问题总结 在嵌入式系统和物联网&#xff08;IoT&#xff09;设备中&#xff0c;串行通信是一种非常普遍且重要的数据传输方式。无论是通过 UART、RS-232…

【Godot4.3】基于中心点连线的矩形重叠检测

概述 这个方法是我自己想到的&#xff0c;经典的矩形重叠&#xff08;碰撞&#xff09;检测&#xff0c;是一段很复杂的逻辑判断&#xff0c;而根据两个矩形中点连线&#xff0c;与两个矩形宽度和高度之和一半的比较&#xff0c;就可以判断两个矩形是否重叠&#xff0c;并且能…

SQL进阶技巧:统计各时段观看直播的人数

目录 0 需求描述 1 数据准备 2 问题分析 3 小结 如果觉得本文对你有帮助&#xff0c;那么不妨也可以选择去看看我的博客专栏 &#xff0c;部分内容如下&#xff1a; 数字化建设通关指南 专栏 原价99&#xff0c;现在活动价39.9&#xff0c;十一国庆后将上升至59.9&#…

TransFormer 视频笔记

TransFormer BasicsAttention单头注意力 single head attentionQ&#xff1a; query 查寻矩阵 128*12288K key matrix 128*12288SoftMax 归一 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/19e3cf1ea28442eca60d5fc1303921f4.png)Value matrix 12288*12288 MLP Bas…