132. Flood Fill with BFS
Problem
Perform the same four-neighbor region recoloring as kata 131, but use a FIFO queue for breadth-first exploration. Mutate and return the image. The unchanged-color and empty-input policies remain the same.
Examples
Example 1
flood_fill_bfs([[1,1,0],[1,0,1]], 0, 0, 2)
=> [[2, 2, 0], [2, 0, 1]]Traversal order changes but the component does not.
Example 2
flood_fill_bfs([[3]], 0, 0, 3)
=> [[3]]The image already has the requested color.
Example 3
flood_fill_bfs([], 0, 0, 9)
=> []There is no starting cell to explore.
Reveal the worked solution and reasoning
Why this mechanism works
Changing the worklist changes exploration order, not component membership. FIFO expands cells in nondecreasing hop distance from the start, while LIFO follows a deeper frontier first. Marking at enqueue time remains essential in either traversal.
State and boundaries
queue contains discovered recolored cells awaiting expansion in discovery order. original is the target component color.
Protocol
- Handle empty or unchanged-color input.
- Recolor and enqueue the start.
- Remove the oldest queued cell.
- Discover original-color neighbors by recoloring before enqueue.
- Continue until the queue empties.
Visualization
start -> distance 1 neighbors -> distance 2 neighbors
FIFO preserves this layer order
final recolored component matches DFSPython implementation
from collections import deque
def flood_fill_bfs(image, sr, sc, color):
if not image or not image[0]:
return image
original = image[sr][sc]
if original == color:
return image
rows, cols = len(image), len(image[0])
queue = deque([(sr, sc)])
image[sr][sc] = color
while queue:
r, c = queue.popleft()
for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and image[nr][nc] == original:
image[nr][nc] = color
queue.append((nr, nc))
return image
Worked execution
flood_fill_bfs([[1,1,0],[1,0,1]], 0, 0, 2)
L4 flood_fill_bfs: if not image or not image[0]: image=[[1, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2 L6 flood_fill_bfs: original = image[sr][sc] image=[[1, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1 L9 flood_fill_bfs: rows, cols = len(image), len(image[0]) image=[[1, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3 L10 flood_fill_bfs: queue = deque([(sr, sc)]) image=[[1, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([(0, 0)]) L11 flood_fill_bfs: image[sr][sc] = color image=[[2, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([(0, 0)]) L13 flood_fill_bfs: r, c = queue.popleft() image=[[2, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=0 L14 flood_fill_bfs: for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)): image=[[2, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=0; dr=1; dc=0 L15 flood_fill_bfs: nr, nc = r + dr, c + dc image=[[2, 1, 0], [1, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=0; dr=1; dc=0; nr=1; nc=0 ... 24 intermediate state changes omitted ... L14 flood_fill_bfs: for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)): image=[[2, 2, 0], [2, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=1; dr=0; dc=1; nr=-1; nc=1 L15 flood_fill_bfs: nr, nc = r + dr, c + dc image=[[2, 2, 0], [2, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=1; dr=0; dc=1; nr=0; nc=2 L14 flood_fill_bfs: for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)): image=[[2, 2, 0], [2, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=1; dr=0; dc=-1; nr=0; nc=2 L15 flood_fill_bfs: nr, nc = r + dr, c + dc image=[[2, 2, 0], [2, 0, 1]]; sr=0; sc=0; color=2; original=1; rows=2; cols=3; queue=deque([]); r=0; c=1; dr=0; dc=-1; nr=0; nc=0
Result: [[2, 2, 0], [2, 0, 1]]. Traversal order changes but the component does not.
flood_fill_bfs([[3]], 0, 0, 3)
L4 flood_fill_bfs: if not image or not image[0]: image=[[3]]; sr=0; sc=0; color=3 L6 flood_fill_bfs: original = image[sr][sc] image=[[3]]; sr=0; sc=0; color=3; original=3
Result: [[3]]. The image already has the requested color.
Why the solution is correct
The same component reachability argument as DFS proves that exactly the connected original-color region is recolored. FIFO order additionally ensures each discovery layer is expanded before the next layer, which will matter when distance becomes the output.
Time and space
O(R*C) worst-case time and O(R*C) queue space.
Common mistakes
Using list.pop(0) introduces shifting cost. Switching to a queue does not justify delaying the discovery mark until removal.
Reasoning card
State: queue contains discovered recolored cells awaiting expansion in discovery order. original is the target component color.
Invariant: Every queued cell is valid, reachable, and scheduled once.
Decision to rehearse: Mark when enqueueing, not when dequeuing.
Finish: Continue until the queue empties
Transfer exercise
Return the number of BFS layers needed to reach the farthest cell in the recolored component.