Loading problems...
TL;DR: The optimal solution uses Depth-First Search (DFS) to traverse and update the connected component of pixels matching the starting color.
The problem requires modifying a specific region of a 2D grid representing an image. Given a starting pixel (sr, sc) and a target color, we must change the color of the starting pixel and all 4-directionally connected pixels that share the same initial color. This process repeats recursively for the neighbors of the updated pixels. This is a classic implementation of the Flood Fill algorithm, a popular interview question often found in computer graphics applications.
LeetCode 733 asks us to return the modified grid after all reachable pixels of the original color have been updated to the new color.
A naive approach to solving the Flood Fill problem involves iteratively scanning the entire grid to propagate the color change. Instead of following the connections directly, one might attempt to sweep through the matrix repeatedly.
Naive Algorithm:
originalColor at (sr, sc).image[sr][sc] to the .newColor(i, j) in the grid.image[i][j] is the newColor, check its 4 neighbors.originalColor, change it to the newColor and mark a flag indicating a change occurred.Pseudo-code:
textchanged = true while changed is true: changed = false for r from 0 to m-1: for c from 0 to n-1: if image[r][c] == newColor: for neighbor in getNeighbors(r, c): if image[neighbor] == originalColor: image[neighbor] = newColor changed = true
Complexity Analysis:
Why it fails: While this approach works for very small constraints, it is highly inefficient. The time complexity is quadratic relative to the total number of pixels. For larger grids, this results in a Time Limit Exceeded (TLE). It fails to utilize the structural property of the graph (adjacency), performing redundant checks on pixels that are not part of the active boundary.
The efficient solution relies on the observation that the "flood" only moves from a pixel to its immediate neighbors. We do not need to scan the whole grid; we only need to explore the specific path of connections starting from (sr, sc).
The pattern Graph DFS allows us to explore this connected component deeply before backtracking. The key invariant maintained during traversal is that we only proceed to a node if it satisfies two conditions:
originalColor.By mutating the pixel's color to the newColor immediately upon visitation, we achieve two goals simultaneously:
newColor (and assuming newColor != originalColor), subsequent checks will fail the second condition above, preventing infinite loops and redundant processing.Visual Description:
Imagine the execution as a recursion tree rooted at (sr, sc). When the algorithm processes a pixel, it changes its value and then "branches" out to the pixel's top, bottom, left, and right neighbors.

We will implement a Depth-First Search to traverse the connected component.
Initialization:
originalColor from image[sr][sc].originalColor is already equal to color (the new color), no work is needed. Returning the original image immediately is crucial to avoid infinite recursion (as the "visited" logic relies on the color changing).DFS Function:
dfs(row, col) that takes the current coordinates.row or col are out of grid bounds, or if image[row][col] does not match originalColor. If any of these are true, return immediately.image[row][col] to color.dfs for all four adjacent coordinates: (row-1, col), (row+1, col), (row, col-1), and (row, col+1).Invocation:
dfs function starting at (sr, sc).image.This strategy ensures every reachable pixel of the correct color is visited exactly once.
Let's trace the algorithm with image = [[1,1,1],[1,1,0],[1,0,1]], sr = 1, sc = 1, color = 2.
originalColor is 1. newColor is 2.
dfs(1, 1).image[1][1] is 1 (matches original).image[1][1] to 2. Grid is now [[1,1,1],[1,2,0],[1,0,1]].dfs(0, 1).image[0][1] is 1.image[0][1] to 2.dfs(-1, 1) -> Out of bounds, return.dfs(1, 1) -> Value is 2 (not original 1), return.dfs(0, 0).image[0][0] is 1.image[0][0] to 2.dfs(0, 0) finishes its neighbors, it returns control to dfs(0, 1).dfs(0, 1) continues to its next neighbor (Right: dfs(0, 2)).1s connected to (1, 1) are turned to 2.1 at (2, 2) is never visited because it is separated by 0s.The correctness of this DFS approach relies on the connectivity property of the graph.
originalColor pixels, the DFS will eventually reach it.originalColor to newColor. Since we only recurse on nodes with originalColor, a node cannot be visited twice (assuming originalColor != newColor). The number of pixels is finite (), so the recursion must terminate.cppclass Solution { public: vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) { int originalColor = image[sr][sc]; // If the color is already the target color, no change is needed. // This prevents infinite recursion. if (originalColor == color) { return image; } dfs(image, sr, sc, originalColor, color); return image; } private: void dfs(vector<vector<int>>& image, int r, int c, int originalColor, int newColor) { // Check boundaries if (r < 0 || r >= image.size() || c < 0 || c >= image[0].size()) { return; } // If the current pixel is not the original color, stop. if (image[r][c] != originalColor) { return; } // Update the color image[r][c] = newColor; // Recurse in all 4 directions dfs(image, r + 1, c, originalColor, newColor); dfs(image, r - 1, c, originalColor, newColor); dfs(image, r, c + 1, originalColor, newColor); dfs(image, r, c - 1, originalColor, newColor); } };
javaclass Solution { public int[][] floodFill(int[][] image, int sr, int sc, int color) { int originalColor = image[sr][sc]; // Prevent infinite recursion if the start color is already the target color if (originalColor == color) { return image; } dfs(image, sr, sc, originalColor, color); return image; } private void dfs(int[][] image, int r, int c, int originalColor, int newColor) { // Boundary checks if (r < 0 || r >= image.length || c < 0 || c >= image[0].length) { return; } // If the pixel does not match the original color, it's not part of the component if (image[r][c] != originalColor) { return; } // Update the pixel color image[r][c] = newColor; // Recurse to neighbors dfs(image, r + 1, c, originalColor, newColor); dfs(image, r - 1, c, originalColor, newColor); dfs(image, r, c + 1, originalColor, newColor); dfs(image, r, c - 1, originalColor, newColor); } }
pythonclass Solution: def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]: original_color = image[sr][sc] rows, cols = len(image), len(image[0]) # If the start pixel is already the target color, return immediately if original_color == color: return image def dfs(r, c): # Check boundaries if r < 0 or r >= rows or c < 0 or c >= cols: return # If pixel is not the original color, stop traversal if image[r][c] != original_color: return # Update color image[r][c] = color # Recurse 4-directionally dfs(r + 1, c) dfs(r - 1, c) dfs(r, c + 1) dfs(r, c - 1) dfs(sr, sc) return image
Let be the number of rows and be the number of columns in the image. Total pixels .
Time Complexity:
In the worst case, every pixel in the grid matches the originalColor. We visit each pixel exactly once to update its color and perform a constant number of checks (4 directions) for each. Thus, the time is proportional to the total number of pixels.
Space Complexity: The space complexity is determined by the recursion stack depth. In the worst case (e.g., a spiral or snake-like pattern of the same color), the recursion depth can go up to . If the connected component is small or balanced, the depth is smaller, but worst-case analysis dictates linear space relative to the grid size.
The Graph DFS - Connected Components pattern used in LeetCode 733 is fundamental for many matrix problems.
Q: Why do I get a Stack Overflow Error?
A: This usually happens if you fail to handle the case where originalColor == newColor. If the target color is the same as the current color, the condition image[r][c] == originalColor will always be true, and the "visited" state (the color change) never effectively happens. The algorithm will recurse infinitely on the same pixels.
Q: Why does my solution modify pixels that aren't connected?
A: This occurs if you are iterating through the entire matrix and checking neighbors naively without a proper traversal logic (like the brute force approach), or if your recursion logic mistakenly jumps across boundaries (e.g., wrapping around the grid edges). Ensure you check r >= 0, r < rows, c >= 0, and c < cols strictly.
Q: Can I use BFS instead of DFS? A: Yes. BFS is equally valid and has the same time complexity. However, for this specific problem, DFS is often preferred in interviews for its concise implementation. The mistake here is assuming one is strictly "better" for performance; they are equivalent for complete traversal, though BFS guarantees finding the shortest path (irrelevant here) and uses a Queue instead of the Call Stack.
Q: Why is my solution TLE (Time Limit Exceeded)? A: You might be re-visiting nodes. In graph traversal, you must mark nodes as visited. In this problem, changing the color serves as the "visited" marker. If you check the color after the recursive call instead of before, or if you don't update the color before recursing, you will process the same nodes exponentially many times.