[Medium] 994. Rotting Oranges
You are given an m x n grid where each cell can have one of three values:
0representing an empty cell,1representing a fresh orange, or2representing a rotten orange.
Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.
Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.
Examples
Example 1:
Input: grid = [[2,1,1],[1,1,0],[0,1,1]]
Output: 4
Example 2:
Input: grid = [[2,1,1],[0,1,1],[1,0,1]]
Output: -1
Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, because rotting only happens 4-directionally.
Example 3:
Input: grid = [[0,2]]
Output: 0
Explanation: Since there are no fresh oranges at minute 0, the answer is just 0.
Constraints
m == grid.lengthn == grid[i].length1 <= m, n <= 10grid[i][j]is0,1, or2.
Thinking Process
- Multi-source BFS: Start from all rotten oranges simultaneously
- BFS visits nodes in non-decreasing distance from the source.
- Queue guarantees shortest path in unweighted graphs.
- Process level by level when counting layers or distances.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Queue BFS (this problem) | O(n) | O(n) | Shortest path in unweighted graphs |
| Multi-source BFS | O(n) | O(n) | Start from all sources simultaneously |
| 0-1 BFS / deque | O(n) | O(n) | Weights 0 or 1 |
| Level-order BFS | O(n) | O(w) | Process by depth/layer |
Solution
Time Complexity: O(m × n) - Each cell is visited at most once
Space Complexity: O(m × n) - For the queue
This solution uses BFS starting from all rotten oranges simultaneously. A special marker {-1, -1} is used to separate levels (minutes).
from collections import deque
class Solution:
def orangesRotting(self, grid):
cache = deque()
freshOranges = 0
ROWS = len(grid)
COLS = len(grid[0])
# Find all rotten oranges and count fresh oranges
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == 2:
cache.append((r, c))
elif grid[r][c] == 1:
freshOranges += 1
# Add level separator
cache.append((-1, -1))
minutesElapsed = -1
dirs = [(-1, 0), (1, 0), (0, 1), (0, -1)]
while cache:
row, col = cache.popleft()
if row == -1:
# Level separator encountered
minutesElapsed += 1
if cache:
# Add separator for next level
cache.append((-1, -1))
else:
# Process current rotten orange
for d in dirs:
nr = row + d[0]
nc = col + d[1]
if (0 <= nr < ROWS and 0 <= nc < COLS and
grid[nr][nc] == 1):
grid[nr][nc] = 2 # Mark as rotten
freshOranges -= 1
cache.append((nr, nc))
return minutesElapsed if freshOranges == 0 else -1
Solution Explanation
Approach: Queue BFS (this problem)
Key idea: 1. Multi-source BFS: Start from all rotten oranges simultaneously
How the code works:
- Multi-source BFS: Start from all rotten oranges simultaneously
- BFS visits nodes in non-decreasing distance from the source.
- Queue guarantees shortest path in unweighted graphs.
- Process level by level when counting layers or distances.
Walkthrough — input grid = [[2,1,1],[1,1,0],[0,1,1]], expected output 4:
- Initialize variables from the problem setup.
- Apply the main loop / recursion until the condition is met.
- Confirm the result matches the expected output.
| Operation | Time | Space |
|---|---|---|
| Initial scan | O(m×n) | O(1) |
| BFS traversal | O(m×n) | O(m×n) |
| Overall | O(m×n) | O(m×n) |
How Solution 1 Works
- Initialization:
- Find all rotten oranges (value 2) and add them to queue
- Count all fresh oranges (value 1)
- Add level separator
{-1, -1}after initial rotten oranges
- BFS Processing:
- When encountering
{-1, -1}, increment minutes and add separator for next level - For each rotten orange, check 4 neighbors
- If neighbor is fresh, mark it as rotten, decrement fresh count, and add to queue
- When encountering
- Result:
- If all fresh oranges are rotten (
freshOranges == 0), return minutes elapsed - Otherwise, return -1 (impossible to rot all oranges)
- If all fresh oranges are rotten (
Key Insight
The {-1, -1} marker acts as a level separator:
- All oranges at the same level (same minute) are processed together
- When we encounter the marker, we know we’ve finished one minute
- This allows us to track time without maintaining a separate distance/time array
Example Walkthrough
Input: grid = [[2,1,1],[1,1,0],[0,1,1]]
Solution 1 (Marker-based):
Initial:
Rotten: (0,0)
Fresh: 5 oranges
Queue: [(0,0), (-1,-1)]
Minute 0:
Process (0,0): Rot (0,1) and (1,0)
Queue: [(-1,-1), (0,1), (1,0)]
Fresh: 3
Minute 1:
Process (0,1): Rot (0,2)
Process (1,0): Rot (1,1)
Queue: [(-1,-1), (0,2), (1,1)]
Fresh: 1
Minute 2:
Process (0,2): No new rots
Process (1,1): Rot (2,1)
Queue: [(-1,-1), (2,1)]
Fresh: 0
Minute 3:
Process (2,1): Rot (2,2)
Queue: [(-1,-1), (2,2)]
Fresh: 0
Minute 4:
Process (2,2): No new rots
Queue: [(-1,-1)]
Fresh: 0
Result: 4 minutes
Complexity
| Operation | Time | Space | |———–|——|——-| | Initial scan | O(m×n) | O(1) | | BFS traversal | O(m×n) | O(m×n) | | Overall | O(m×n) | O(m×n) |
Common Mistakes
- No fresh oranges:
[[0,2]]→ return 0 - Impossible to rot all: Isolated fresh orange → return -1
- All rotten initially:
[[2,2]]→ return 0 - All fresh initially: No rotten oranges → return -1
-
Empty grid: Not possible per constraints
- Not counting initial fresh oranges: Must count before BFS starts
- Wrong level tracking: Forgetting to increment minutes at right time
- Boundary checks: Not checking array bounds before accessing
- Visited tracking: Not marking as rotten immediately (could process same cell twice)
- Return value: Returning minutes when freshOranges > 0 (should return -1)
Optimization Tips
- Early termination: Can break early if
freshOranges == 0during BFS - Space optimization: Solution 1 uses no extra space beyond queue
- Level tracking: Marker approach is elegant but level-size approach is clearer
Related Problems
- 286. Walls and Gates - Similar multi-source BFS
- 317. Shortest Distance from All Buildings - Multi-source BFS with distance
- 200. Number of Islands - Connected components
- 542. 01 Matrix - Distance from nearest 0
- 1162. As Far from Land as Possible - Multi-source BFS
Pattern Recognition
This problem demonstrates the “Multi-Source BFS” pattern:
1. Find all starting points (sources)
2. Add all sources to queue
3. Process level by level
4. Track time/distance from sources
5. Check if all targets are reached
Similar problems:
- Walls and Gates
- Shortest Distance from All Buildings
- 01 Matrix
- As Far from Land as Possible
Real-World Applications
- Disease Spread: Model how disease spreads from multiple sources
- Network Broadcasting: Broadcast message from multiple nodes
- Fire Spread: Simulate fire spreading from multiple ignition points
- Virus Propagation: Model computer virus spreading in network
- Information Diffusion: Track information spread in social networks
References
- LC 994: Rotting Oranges on LeetCode
- LeetCode Discuss — LC 994: Rotting Oranges
- LeetCode Editorial (may require premium)
Key Takeaways
- Multi-source BFS: Start from all rotten oranges simultaneously
- Level separation: Need to track time/levels to know when all oranges at current level are processed
- Fresh count tracking: Decrement count when rotting, check if zero at end
- Grid modification: Mark as rotten immediately to avoid reprocessing