news 2026/7/29 1:35:51

LeetCode 994. 腐烂的橘子

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 994. 腐烂的橘子

题目描述

给定一个m x n的网格grid,每个单元格可能有三种值:

  • 0表示空单元格。
  • 1表示新鲜橘子。
  • 2表示腐烂的橘子。

每分钟,腐烂的橘子会让上下左右四个方向相邻的新鲜橘子腐烂。

要求返回直到没有新鲜橘子为止所需的最小分钟数。如果存在新鲜橘子永远无法腐烂,返回-1

初始思路

一开始容易想到 DFS:从每个腐烂橘子出发,去感染周围的新鲜橘子。

但这个思路有一个关键问题:题目里的腐烂过程是“每分钟同时扩散”,而普通 DFS 更像是一条路径一直往深处走。DFS 不能自然表达“第 1 分钟腐烂哪些橘子,第 2 分钟腐烂哪些橘子”。

比如最开始写成“遇到腐烂橘子,就把周围一圈新鲜橘子变腐烂”,这其实只模拟了 1 分钟,并没有继续分层扩散,也没有正确统计答案。

所以这题更适合使用多源 BFS

解题思路

把网格看成一张图:

  • 每个橘子格子可以看作一个节点。
  • 上下左右相邻表示节点之间有边。
  • 所有初始腐烂橘子2都是 BFS 的起点。

因为一开始可能有多个腐烂橘子,而且它们会同时向外扩散,所以不能只从一个点开始 BFS,而是要把所有初始腐烂橘子一起加入队列。这就是多源 BFS。

具体流程:

  1. 遍历整个grid
  2. 遇到腐烂橘子2,加入队列。
  3. 遇到新鲜橘子1,统计数量fresh++
  4. BFS 每次处理当前队列中的size个橘子。
  5. 这一批橘子代表同一分钟内会继续扩散的腐烂橘子。
  6. 如果感染到新鲜橘子,就把它改成2,同时fresh--,再加入队列。
  7. BFS 结束后,如果fresh == 0,说明所有新鲜橘子都腐烂了;否则返回-1

为什么要按层 BFS

BFS 中的“一层”正好对应题目中的“一分钟”。

例如:

2 1 1 1 1 0 0 1 1

第 1 分钟,初始腐烂橘子影响周围:

2 2 1 2 1 0 0 1 1

第 2 分钟,新腐烂的橘子继续扩散:

2 2 2 2 2 0 0 1 1

所以代码里需要先记录当前队列大小:

int size = queue.size();

然后只处理这size个元素。新加入队列的橘子不能在同一分钟继续扩散,而是留到下一轮处理。

易错点

1. Java 数组入队写法

Java 中不能这样写:

queue.add({i, j});

应该写成:

queue.add(new int[]{i, j});

因为{i, j}只能在数组初始化语境中使用,不能单独作为一个对象传入方法。

2. BFS 循环条件

如果写成:

while (!queue.isEmpty()) { minutes++; }

答案可能会多 1。

原因是最后一批刚刚腐烂的橘子还会留在队列里,再被处理一轮。但这一轮已经没有新的新鲜橘子可以腐烂了,不应该再增加分钟数。

更稳妥的写法是:

while (!queue.isEmpty() && fresh > 0) { minutes++; }

只有在还存在新鲜橘子时,继续按分钟扩散。

3. 最后要判断是否还有新鲜橘子

BFS 结束不代表所有新鲜橘子都腐烂了。

如果某些新鲜橘子被空格隔开,永远无法被腐烂橘子感染,就需要返回-1

所以最后要根据fresh判断:

return fresh == 0 ? minutes : -1;

代码实现

class Solution { int[][] D = { { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 } }; int m; int n; public int orangesRotting(int[][] grid) { m = grid.length; n = grid[0].length; Deque<int[]> queue = new ArrayDeque<>(); int fresh = 0; int minutes = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 2) { queue.add(new int[]{i, j}); } else if (grid[i][j] == 1) { fresh++; } } } while (!queue.isEmpty() && fresh > 0) { int size = queue.size(); minutes++; for (int i = 0; i < size; i++) { int[] cur = queue.poll(); for (int[] d : D) { int x = cur[0] + d[0]; int y = cur[1] + d[1]; if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) { grid[x][y] = 2; fresh--; queue.add(new int[]{x, y}); } } } } return fresh == 0 ? minutes : -1; } }

复杂度分析

  • 时间复杂度:O(m * n)。每个格子最多入队一次,最多被检查一次。
  • 空间复杂度:O(m * n)。最坏情况下,队列中可能存放大量腐烂橘子。

复盘

这题的关键不是“会不会遍历四个方向”,而是能不能看出它是一个按时间分层扩散的问题。

当题目出现“每分钟”“同时扩散”“最短时间”这类描述时,要优先考虑 BFS。并且如果一开始有多个源头,比如多个腐烂橘子,就要想到多源 BFS。

下次写类似题时,可以先检查三点:

  • 是否把所有起点都加入队列,而不是只从一个点开始。
  • 是否用size = queue.size()区分每一分钟。
  • 是否用剩余数量fresh判断答案和-1
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/29 1:34:05

SpringBoot+Vue酒店客房管理系统开发实战

1. 项目概述这个基于SpringBootVue的酒店客房管理系统是一个典型的Java Web毕业设计项目&#xff0c;它完整实现了酒店客房管理的核心业务流程。作为一个前后端分离架构的实战案例&#xff0c;它涵盖了从数据库设计到前端展示的全套解决方案&#xff0c;非常适合计算机相关专业…

作者头像 李华
网站建设 2026/7/29 1:25:57

SpringBoot+Vue+MySQL电商系统与协同过滤算法实践

1. 项目概述黔醉酒业白酒销售系统是一个基于SpringBootVueMySQL技术栈的电商平台&#xff0c;主要面向白酒销售行业。系统采用前后端分离架构&#xff0c;后端使用SpringBoot框架提供RESTful API接口&#xff0c;前端采用Vue.js构建用户界面&#xff0c;数据库选用MySQL存储业务…

作者头像 李华
网站建设 2026/7/29 1:24:04

Azure Stack Hub 网络服务管理工具与常见问题排错(下篇)

未经同意&#xff0c;请勿转载&#xff01; 本篇定位&#xff1a;面向 Azure Stack Hub 一线运维 / SOC / 监控 / 故障响应工程师。本文承接上篇《Azure Stack Hub 网络服务&#xff1a;从物理拓扑到租户 SDN 完整图谱》中网络模型部分&#xff0c;重点展示如何在生产中观测、运…

作者头像 李华
网站建设 2026/7/29 1:20:13

营销活动前如何利用GEO监测降低试错风险与资源浪费?

很多企业在准备营销活动时&#xff0c;注意力都放在了关键词排名、点击流量或社交媒体的曝光量上。然而&#xff0c;随着生成式AI逐渐成为用户获取信息的核心入口&#xff0c;营销预算投出去了&#xff0c;却发现品牌在AI回答中根本没被提及&#xff0c;或者推荐的理由完全对不…

作者头像 李华
网站建设 2026/7/29 1:19:45

kill-doc:打破文档获取壁垒的开源浏览器脚本解决方案

kill-doc&#xff1a;打破文档获取壁垒的开源浏览器脚本解决方案 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档&#xff0c;但是相关网站浏览体验不好各种广告&#xff0c;各种登录验证&#xff0c;需要很多步骤才能下载文档&#xff0c;该脚本就是为了解…

作者头像 李华
网站建设 2026/7/29 1:19:40

小学生AI编程启蒙:用Python循环批量处理Prompt任务

1. 项目概述&#xff1a;当小学生遇上AI批量任务去年暑假&#xff0c;我在社区图书馆开设了一个面向小学生的AI启蒙工作坊。当10岁的乐乐用3行代码让AI批量生成20首不同风格的儿童诗时&#xff0c;整个教室爆发出的欢呼声让我意识到&#xff1a;Prompt结合循环结构&#xff0c;…

作者头像 李华