Problem Statement

Rotting Oranges

You have a grid of boxes (m rows by n columns). Each box holds one of three things: 0 means the box is empty, 1 means it has a fresh orange, and 2 means it has a rotten orange. Every minute, rot spreads. Any fresh orange sitting right next to a rotten one (up, down, left, or right, not diagonally) turns rotten too. Your job is to find the smallest number of minutes until no fresh orange is left. If some fresh orange can never be reached by the rot, return -1. The tool that fits this is BFS, short for breadth-first search. Picture dropping a stone in a pond. The ripple spreads out one ring at a time: first the cells touching the stone, then the cells touching those, and so on. BFS works the same way, exploring everything one step away, then everything two steps away. Here, "one step" is exactly "one minute of rotting." That is why BFS fits: each ring of the ripple is one minute. The twist is that we have many rotten oranges starting at once, not one. So we drop many stones at the same time. That is called multi-source BFS ("multi-source" just means more than one starting point), and all the ripples grow together, so the count of rings still equals the number of minutes.

mediumGraphBFSGraphsTime: O(m*n) · Space: O(m*n)

Signals to notice

multi-source BFSspreading from all rotten oranges simultaneouslyminimum time

Brute force first

Simulate minute by minute from each rotten orange independently. Re-processes already-rotten oranges. That direct path helps you understand the question, but it tends to treat every possibility as brand new instead of learning from earlier steps.

The key insight

Multi-source BFS: start with ALL rotten oranges in the queue simultaneously. Each BFS level = one minute of spreading. Count levels until all fresh oranges are rotten. Instead of recomputing the world every time, you preserve just enough context to let the next decision become obvious.

Trace it on grid=[[2,1,1],[1,1,0],[0,1,1]]

init: scan grid -> queue=[(0,0)], fresh=6, minutes=0
min 1: pop (0,0); rot (0,1),(1,0); queue=[(0,1),(1,0)], fresh=4
min 2: level={(0,1),(1,0)}; rot (0,2),(1,1); queue=[(0,2),(1,1)], fresh=2
min 3: level={(0,2),(1,1)}; (0,2) none, rot (2,1); queue=[(2,1)], fresh=1
min 4: level={(2,1)}; rot (2,2); queue=[(2,2)], fresh=0
loop exits (fresh==0); return minutes=4

What must stay true

All rotten oranges spread simultaneously — that's multi-source BFS. Each level of the BFS represents one minute. The number of levels (minus 1) is the answer. As long as that statement keeps holding, you can trust the steps built on top of it.

Shape of the loop

enqueue all rotten cells; count fresh
while queue not empty and fresh > 0:
    minutes += 1
    for each cell in current level (snapshot size):
        for each 4-neighbor that is fresh:
            mark rotten; fresh -= 1; enqueue it
return fresh == 0 ? minutes : -1

Pseudocode only — the full worked solution lives in the Solution tab.

Easy way to go wrong

Starting BFS from only one rotten orange — all rotten oranges spread in parallel. Initialize the queue with ALL of them. When the code becomes mechanical before the idea is clear, small edge cases start breaking the whole story.

Graphs Pattern