LeetCode 994腐烂橘子问题:DFS解法超时的优化方案咨询
LeetCode 994腐烂橘子问题:DFS解法超时的优化方案咨询
嗨,我来帮你捋捋这个问题的优化思路哈~
先给你提个小细节:你写的那个dfs函数其实是BFS实现哦(用了队列+popleft(),这是典型的广度优先搜索逻辑),不过这不是核心问题——你的解法超时的根本原因是重复计算量太大。
先分析下超时的根源
你当前的思路是对每个新鲜橘子(值为1的单元格)单独跑一遍BFS,去找最近的烂橘子需要多久能感染它。假设网格里有k个新鲜橘子,每个BFS最坏情况下要遍历O(mn)个单元格,整体时间复杂度直接冲到O(k*mn)。像你给的10x10例子里,大部分都是新鲜橘子,重复计算的次数直接爆炸,超时是必然的。
最优优化方案:多源BFS(从烂橘子出发反向扩散)
咱们换个思路,从所有初始烂橘子同时出发,主动扩散感染新鲜橘子,这就是多源BFS,整个过程只需要遍历网格一次,时间复杂度是O(mn),完全不会超时。具体逻辑是:
- 第一步:先遍历整个网格,把所有初始烂橘子(值为2)的位置加入队列,同时记录它们的初始时间为0;另外统计新鲜橘子的总数,方便最后判断是否有无法感染的橘子。
- 第二步:用BFS逐层扩散,每次从队列里取出一个烂橘子,向上下左右四个方向检查相邻单元格:
- 如果是新鲜橘子(值为1),就把它标记为烂橘子,感染时间设为当前烂橘子的时间+1,然后把这个新烂橘子加入队列,同时新鲜橘子总数减1。
- 第三步:BFS结束后,如果还有新鲜橘子没被感染(总数>0),返回-1;否则返回记录的最大感染时间。
具体代码实现
给你写个可直接用的示例代码:
import collections def orangesRotting(grid): m, n = len(grid), len(grid[0]) q = collections.deque() fresh_count = 0 max_time = 0 # 初始化队列:把所有初始烂橘子的位置和初始时间0加入队列 for i in range(m): for j in range(n): if grid[i][j] == 2: q.append((i, j, 0)) elif grid[i][j] == 1: fresh_count += 1 directions = [(1,0), (-1,0), (0,1), (0,-1)] # 开始BFS扩散 while q and fresh_count > 0: x, y, time = q.popleft() for dx, dy in directions: nx, ny = x + dx, y + dy # 检查相邻单元格是否是未感染的新鲜橘子 if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == 1: grid[nx][ny] = 2 # 标记为已腐烂 fresh_count -= 1 current_time = time + 1 max_time = max(max_time, current_time) q.append((nx, ny, current_time)) # 还有新鲜橘子没被感染就返回-1,否则返回最大时间 return max_time if fresh_count == 0 else -1
为啥这个方法不会超时?
这个方法里每个单元格最多被访问一次:烂橘子被从队列取出来处理一次,新鲜橘子被感染一次后就不会再被重复处理,完全没有冗余计算。你之前担心“反转新鲜和烂橘子也会超时”,其实是误解了——多源BFS是从所有源点同时出发,不管源点数量多少,都是一次遍历,和你原来单个新鲜橘子跑BFS的重复计算完全不是一回事~
再补个小提醒
你把用队列实现的BFS叫成了DFS,这里要注意区分:DFS是深度优先(常用递归或栈实现),BFS是广度优先(常用队列实现),找最短路径/最短时间这类问题,BFS本来就比DFS更合适,不过你原来的用法是每个新鲜橘子单独跑BFS,才导致的超时。
备注:内容来源于stack exchange,提问作者WaterDrop




