302. Smallest Rectangle Enclosing Black Pixels 🔒
Problem Description
You have a binary matrix image
of size m x n
where each cell contains either '0'
(white pixel) or '1'
(black pixel).
The key properties of this matrix are:
- All black pixels (
'1'
) form a single connected region - Black pixels are connected horizontally and vertically (not diagonally)
- You're given coordinates
(x, y)
of one black pixel in this region
Your task is to find the area of the smallest axis-aligned rectangle that can enclose all the black pixels in the matrix.
For example, if you have black pixels scattered in various positions, you need to find the tightest bounding box that contains all of them. The area would be calculated as (height × width)
of this bounding rectangle.
Important constraint: Your algorithm must run in less than O(mn)
time complexity, meaning you cannot simply scan through every cell in the matrix.
The solution uses binary search to efficiently find the boundaries of the black pixel region:
- Finds the topmost row containing a black pixel (upper boundary
u
) - Finds the bottommost row containing a black pixel (lower boundary
d
) - Finds the leftmost column containing a black pixel (left boundary
l
) - Finds the rightmost column containing a black pixel (right boundary
r
)
The final area is calculated as (d - u + 1) * (r - l + 1)
, representing the height times the width of the bounding rectangle.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: While the problem presents as a 2D matrix, we can conceptualize it as a graph where each pixel is a node and adjacent pixels (horizontally and vertically) form edges. The black pixels form a connected component in this graph.
Is it a tree?
- No: The pixel connectivity structure is not a tree - it can have cycles. For example, four black pixels forming a square would create a cycle.
Is the problem related to directed acyclic graphs?
- No: The pixel connectivity is undirected (if pixel A connects to pixel B, then B connects to A).
Is the problem related to shortest paths?
- No: We're not finding shortest paths between pixels. We're finding the boundaries of a connected region.
Does the problem involve connectivity?
- Yes: The core property is that all black pixels form a single connected region. We need to explore this connected component to find its boundaries.
Does the problem have small constraints?
- No: The problem explicitly requires less than O(mn) complexity, suggesting the constraints are not small enough for exhaustive exploration.
Conclusion: Following the flowchart, we arrive at BFS as a potential solution. However, the problem's constraint of less than O(mn) complexity means we cannot use traditional DFS/BFS to explore all black pixels.
Note: While the flowchart suggests BFS for connectivity problems with larger constraints, the actual solution uses binary search instead of DFS/BFS traversal. This is because:
- We know there's only one connected black region
- We have a starting black pixel coordinate (x, y)
- Binary search can find boundaries in O(m log n + n log m) time, which satisfies the complexity requirement
The DFS pattern would typically be used if we needed to explore the entire connected component, but here the optimization comes from using binary search to find the extreme boundaries without visiting every black pixel.
Intuition
The key insight comes from understanding what we're really looking for and the properties of the black pixel region.
Since all black pixels form a single connected region, and we need the smallest rectangle that encloses them all, we're essentially finding the extreme boundaries of this region - the topmost, bottommost, leftmost, and rightmost black pixels.
The naive approach would be to perform DFS/BFS from the given black pixel (x, y)
to explore all connected black pixels and track the min/max coordinates. However, this would take O(mn)
time in the worst case, violating our complexity requirement.
Here's the crucial observation: If we know there's at least one black pixel in a row or column, we can use binary search to find the boundary rows and columns.
Think about finding the topmost row with a black pixel:
- We know row
x
has a black pixel (given) - We know rows above some boundary have no black pixels
- We know rows at or below the boundary have at least one black pixel
- This creates a binary property we can search on!
For each boundary:
- Upper boundary: Binary search rows
[0, x]
to find the smallest row index that contains a black pixel - Lower boundary: Binary search rows
[x, m-1]
to find the largest row index that contains a black pixel - Left boundary: Binary search columns
[0, y]
to find the smallest column index that contains a black pixel - Right boundary: Binary search columns
[y, n-1]
to find the largest column index that contains a black pixel
To check if a row has a black pixel, we scan through all columns in that row (O(n)
time). Similarly, checking a column takes O(m)
time.
This gives us a total complexity of O(m log n + n log m)
:
- Finding horizontal boundaries: 2 binary searches ×
O(log m)
iterations ×O(n)
per check - Finding vertical boundaries: 2 binary searches ×
O(log n)
iterations ×O(m)
per check
The area of the bounding rectangle is then simply (bottom - top + 1) × (right - left + 1)
.
Solution Approach
The implementation uses binary search to find the four boundaries of the black pixel region. Let's walk through each part:
Finding the Upper Boundary (topmost row with black pixel):
left, right = 0, x while left < right: mid = (left + right) >> 1 c = 0 while c < n and image[mid][c] == '0': c += 1 if c < n: # Found a black pixel in row mid right = mid else: # No black pixel in row mid left = mid + 1 u = left
- Search range:
[0, x]
(we know rowx
has a black pixel) - For each
mid
row, scan all columns to check if any black pixel exists - If found (
c < n
), the boundary is atmid
or above, so moveright = mid
- If not found, the boundary is below
mid
, so moveleft = mid + 1
Finding the Lower Boundary (bottommost row with black pixel):
left, right = x, m - 1 while left < right: mid = (left + right + 1) >> 1 c = 0 while c < n and image[mid][c] == '0': c += 1 if c < n: # Found a black pixel in row mid left = mid else: # No black pixel in row mid right = mid - 1 d = left
- Search range:
[x, m-1]
- Note the
mid = (left + right + 1) >> 1
to avoid infinite loop when finding maximum - If black pixel found, boundary is at
mid
or below, so moveleft = mid
- If not found, boundary is above
mid
, so moveright = mid - 1
Finding the Left Boundary (leftmost column with black pixel):
left, right = 0, y while left < right: mid = (left + right) >> 1 r = 0 while r < m and image[r][mid] == '0': r += 1 if r < m: # Found a black pixel in column mid right = mid else: # No black pixel in column mid left = mid + 1 l = left
- Search range:
[0, y]
(we know columny
has a black pixel) - For each
mid
column, scan all rows to check if any black pixel exists - Similar binary search logic as upper boundary
Finding the Right Boundary (rightmost column with black pixel):
left, right = y, n - 1 while left < right: mid = (left + right + 1) >> 1 r = 0 while r < m and image[r][mid] == '0': r += 1 if r < m: # Found a black pixel in column mid left = mid else: # No black pixel in column mid right = mid - 1 r = left
- Search range:
[y, n-1]
- Similar to finding the lower boundary, using
mid = (left + right + 1) >> 1
Calculating the Area:
return (d - u + 1) * (r - l + 1)
- Height:
d - u + 1
(inclusive of both boundaries) - Width:
r - l + 1
(inclusive of both boundaries)
The bit shift operation >> 1
is used instead of division by 2 for efficiency. The algorithm leverages the fact that the black region is connected, ensuring that if we find boundaries correctly, all black pixels will be enclosed within the rectangle defined by these boundaries.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate how the binary search solution finds the bounding rectangle.
Consider this 5×7 matrix with a connected black pixel region:
0 1 2 3 4 5 6 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 0 2 0 1 1 1 0 0 0 3 0 0 1 0 0 0 0 4 0 0 0 0 0 0 0
Given: Starting black pixel at position (x=2, y=3)
(row 2, column 3).
Step 1: Find Upper Boundary (topmost row with black pixel)
- Binary search range:
[0, 2]
(from row 0 to row x) - Iteration 1:
mid = 1
, scan row 1 → find '1' at column 2 → black pixel found- Move
right = 1
, range becomes[0, 1]
- Move
- Iteration 2:
mid = 0
, scan row 0 → all '0's → no black pixel- Move
left = 1
, range becomes[1, 1]
- Move
- Result:
u = 1
(row 1 is the topmost row with black pixels)
Step 2: Find Lower Boundary (bottommost row with black pixel)
- Binary search range:
[2, 4]
(from row x to last row) - Iteration 1:
mid = 3
, scan row 3 → find '1' at column 2 → black pixel found- Move
left = 3
, range becomes[3, 4]
- Move
- Iteration 2:
mid = 4
, scan row 4 → all '0's → no black pixel- Move
right = 3
, range becomes[3, 3]
- Move
- Result:
d = 3
(row 3 is the bottommost row with black pixels)
Step 3: Find Left Boundary (leftmost column with black pixel)
- Binary search range:
[0, 3]
(from column 0 to column y) - Iteration 1:
mid = 1
, scan column 1 → find '1' at row 2 → black pixel found- Move
right = 1
, range becomes[0, 1]
- Move
- Iteration 2:
mid = 0
, scan column 0 → all '0's → no black pixel- Move
left = 1
, range becomes[1, 1]
- Move
- Result:
l = 1
(column 1 is the leftmost column with black pixels)
Step 4: Find Right Boundary (rightmost column with black pixel)
- Binary search range:
[3, 6]
(from column y to last column) - Iteration 1:
mid = 5
, scan column 5 → all '0's → no black pixel- Move
right = 4
, range becomes[3, 4]
- Move
- Iteration 2:
mid = 4
, scan column 4 → find '1' at row 1 → black pixel found- Move
left = 4
, range becomes[4, 4]
- Move
- Result:
r = 4
(column 4 is the rightmost column with black pixels)
Step 5: Calculate Area
- Height =
d - u + 1 = 3 - 1 + 1 = 3
- Width =
r - l + 1 = 4 - 1 + 1 = 4
- Area =
3 × 4 = 12
The bounding rectangle encompasses rows [1,3] and columns [1,4], perfectly enclosing all black pixels:
0 1 2 3 4 5 6 0 0 0 0 0 0 0 0 1 0 [1 1 1 1] 0 0 2 0 [1 1 1 0] 0 0 3 0 [0 1 0 0] 0 0 4 0 0 0 0 0 0 0
Time Complexity Analysis:
- Each binary search performs
O(log m)
orO(log n)
iterations - Each iteration scans a row (
O(n)
) or column (O(m)
) - Total:
O(m log n + n log m)
, which is better than the naiveO(mn)
approach
Solution Implementation
1class Solution:
2 def minArea(self, image: List[List[str]], x: int, y: int) -> int:
3 rows, cols = len(image), len(image[0])
4
5 # Binary search to find the topmost row containing '1'
6 left, right = 0, x
7 while left < right:
8 mid = (left + right) >> 1
9 # Check if current row has any '1'
10 col_idx = 0
11 while col_idx < cols and image[mid][col_idx] == '0':
12 col_idx += 1
13
14 if col_idx < cols: # Found a '1' in this row
15 right = mid # Search upper half
16 else:
17 left = mid + 1 # Search lower half
18 top_boundary = left
19
20 # Binary search to find the bottommost row containing '1'
21 left, right = x, rows - 1
22 while left < right:
23 mid = (left + right + 1) >> 1
24 # Check if current row has any '1'
25 col_idx = 0
26 while col_idx < cols and image[mid][col_idx] == '0':
27 col_idx += 1
28
29 if col_idx < cols: # Found a '1' in this row
30 left = mid # Search lower half (including mid)
31 else:
32 right = mid - 1 # Search upper half
33 bottom_boundary = left
34
35 # Binary search to find the leftmost column containing '1'
36 left, right = 0, y
37 while left < right:
38 mid = (left + right) >> 1
39 # Check if current column has any '1'
40 row_idx = 0
41 while row_idx < rows and image[row_idx][mid] == '0':
42 row_idx += 1
43
44 if row_idx < rows: # Found a '1' in this column
45 right = mid # Search left half
46 else:
47 left = mid + 1 # Search right half
48 left_boundary = left
49
50 # Binary search to find the rightmost column containing '1'
51 left, right = y, cols - 1
52 while left < right:
53 mid = (left + right + 1) >> 1
54 # Check if current column has any '1'
55 row_idx = 0
56 while row_idx < rows and image[row_idx][mid] == '0':
57 row_idx += 1
58
59 if row_idx < rows: # Found a '1' in this column
60 left = mid # Search right half (including mid)
61 else:
62 right = mid - 1 # Search left half
63 right_boundary = left
64
65 # Calculate the area of the bounding rectangle
66 height = bottom_boundary - top_boundary + 1
67 width = right_boundary - left_boundary + 1
68 return height * width
69
1class Solution {
2
3 public int minArea(char[][] image, int x, int y) {
4 int rows = image.length;
5 int cols = image[0].length;
6
7 // Binary search for the topmost row containing '1'
8 int left = 0, right = x;
9 while (left < right) {
10 int mid = (left + right) >> 1;
11 int col = 0;
12 // Check if current row has any '1'
13 while (col < cols && image[mid][col] == '0') {
14 ++col;
15 }
16 if (col < cols) {
17 // Found '1' in this row, search upper half
18 right = mid;
19 } else {
20 // No '1' found, search lower half
21 left = mid + 1;
22 }
23 }
24 int topRow = left;
25
26 // Binary search for the bottommost row containing '1'
27 left = x;
28 right = rows - 1;
29 while (left < right) {
30 int mid = (left + right + 1) >> 1;
31 int col = 0;
32 // Check if current row has any '1'
33 while (col < cols && image[mid][col] == '0') {
34 ++col;
35 }
36 if (col < cols) {
37 // Found '1' in this row, search lower half
38 left = mid;
39 } else {
40 // No '1' found, search upper half
41 right = mid - 1;
42 }
43 }
44 int bottomRow = left;
45
46 // Binary search for the leftmost column containing '1'
47 left = 0;
48 right = y;
49 while (left < right) {
50 int mid = (left + right) >> 1;
51 int row = 0;
52 // Check if current column has any '1'
53 while (row < rows && image[row][mid] == '0') {
54 ++row;
55 }
56 if (row < rows) {
57 // Found '1' in this column, search left half
58 right = mid;
59 } else {
60 // No '1' found, search right half
61 left = mid + 1;
62 }
63 }
64 int leftCol = left;
65
66 // Binary search for the rightmost column containing '1'
67 left = y;
68 right = cols - 1;
69 while (left < right) {
70 int mid = (left + right + 1) >> 1;
71 int row = 0;
72 // Check if current column has any '1'
73 while (row < rows && image[row][mid] == '0') {
74 ++row;
75 }
76 if (row < rows) {
77 // Found '1' in this column, search right half
78 left = mid;
79 } else {
80 // No '1' found, search left half
81 right = mid - 1;
82 }
83 }
84 int rightCol = left;
85
86 // Calculate area of the bounding rectangle
87 return (bottomRow - topRow + 1) * (rightCol - leftCol + 1);
88 }
89}
90
1class Solution {
2public:
3 int minArea(vector<vector<char>>& image, int x, int y) {
4 int rows = image.size();
5 int cols = image[0].size();
6
7 // Binary search for the topmost row containing '1'
8 int left = 0, right = x;
9 while (left < right) {
10 int mid = (left + right) >> 1;
11 int col = 0;
12 // Check if current row has any '1'
13 while (col < cols && image[mid][col] == '0') {
14 ++col;
15 }
16 if (col < cols) { // Found a '1' in this row
17 right = mid; // Search in upper half
18 } else {
19 left = mid + 1; // Search in lower half
20 }
21 }
22 int topRow = left;
23
24 // Binary search for the bottommost row containing '1'
25 left = x;
26 right = rows - 1;
27 while (left < right) {
28 int mid = (left + right + 1) >> 1; // Upper mid for finding maximum
29 int col = 0;
30 // Check if current row has any '1'
31 while (col < cols && image[mid][col] == '0') {
32 ++col;
33 }
34 if (col < cols) { // Found a '1' in this row
35 left = mid; // Search in lower half
36 } else {
37 right = mid - 1; // Search in upper half
38 }
39 }
40 int bottomRow = left;
41
42 // Binary search for the leftmost column containing '1'
43 left = 0;
44 right = y;
45 while (left < right) {
46 int mid = (left + right) >> 1;
47 int row = 0;
48 // Check if current column has any '1'
49 while (row < rows && image[row][mid] == '0') {
50 ++row;
51 }
52 if (row < rows) { // Found a '1' in this column
53 right = mid; // Search in left half
54 } else {
55 left = mid + 1; // Search in right half
56 }
57 }
58 int leftCol = left;
59
60 // Binary search for the rightmost column containing '1'
61 left = y;
62 right = cols - 1;
63 while (left < right) {
64 int mid = (left + right + 1) >> 1; // Upper mid for finding maximum
65 int row = 0;
66 // Check if current column has any '1'
67 while (row < rows && image[row][mid] == '0') {
68 ++row;
69 }
70 if (row < rows) { // Found a '1' in this column
71 left = mid; // Search in right half
72 } else {
73 right = mid - 1; // Search in left half
74 }
75 }
76 int rightCol = left;
77
78 // Calculate the area of the bounding rectangle
79 return (bottomRow - topRow + 1) * (rightCol - leftCol + 1);
80 }
81};
82
1function minArea(image: string[][], x: number, y: number): number {
2 const rows = image.length;
3 const cols = image[0].length;
4
5 // Binary search for the topmost row containing '1'
6 let left = 0;
7 let right = x;
8 while (left < right) {
9 const mid = left + Math.floor((right - left) / 2);
10 let col = 0;
11 // Check if current row has any '1'
12 while (col < cols && image[mid][col] === '0') {
13 col++;
14 }
15 if (col < cols) { // Found a '1' in this row
16 right = mid; // Search in upper half
17 } else {
18 left = mid + 1; // Search in lower half
19 }
20 }
21 const topRow = left;
22
23 // Binary search for the bottommost row containing '1'
24 left = x;
25 right = rows - 1;
26 while (left < right) {
27 const mid = left + Math.floor((right - left + 1) / 2); // Upper mid for finding maximum
28 let col = 0;
29 // Check if current row has any '1'
30 while (col < cols && image[mid][col] === '0') {
31 col++;
32 }
33 if (col < cols) { // Found a '1' in this row
34 left = mid; // Search in lower half (including mid)
35 } else {
36 right = mid - 1; // Search in upper half
37 }
38 }
39 const bottomRow = left;
40
41 // Binary search for the leftmost column containing '1'
42 left = 0;
43 right = y;
44 while (left < right) {
45 const mid = left + Math.floor((right - left) / 2);
46 let row = 0;
47 // Check if current column has any '1'
48 while (row < rows && image[row][mid] === '0') {
49 row++;
50 }
51 if (row < rows) { // Found a '1' in this column
52 right = mid; // Search in left half
53 } else {
54 left = mid + 1; // Search in right half
55 }
56 }
57 const leftCol = left;
58
59 // Binary search for the rightmost column containing '1'
60 left = y;
61 right = cols - 1;
62 while (left < right) {
63 const mid = left + Math.floor((right - left + 1) / 2); // Upper mid for finding maximum
64 let row = 0;
65 // Check if current column has any '1'
66 while (row < rows && image[row][mid] === '0') {
67 row++;
68 }
69 if (row < rows) { // Found a '1' in this column
70 left = mid; // Search in right half (including mid)
71 } else {
72 right = mid - 1; // Search in left half
73 }
74 }
75 const rightCol = left;
76
77 // Calculate the area of the bounding rectangle
78 return (bottomRow - topRow + 1) * (rightCol - leftCol + 1);
79}
80
Time and Space Complexity
Time Complexity: O(m * log n + n * log m)
where m
is the number of rows and n
is the number of columns in the image.
The algorithm performs 4 binary searches:
- Two binary searches on rows (finding upper and lower boundaries): Each binary search takes
O(log m)
iterations, and in each iteration, we scan through alln
columns to check if there's a '1' in that row, takingO(n)
time. Total:2 * O(log m * n) = O(n * log m)
- Two binary searches on columns (finding left and right boundaries): Each binary search takes
O(log n)
iterations, and in each iteration, we scan through allm
rows to check if there's a '1' in that column, takingO(m)
time. Total:2 * O(log n * m) = O(m * log n)
Combined time complexity: O(m * log n + n * log m)
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space for variables (left
, right
, mid
, u
, d
, l
, r
, c
) regardless of the input size. No additional data structures are created that scale with the input dimensions.
Common Pitfalls
1. Incorrect Mid Calculation for Finding Maximum Boundaries
The Pitfall:
When searching for the bottommost row or rightmost column (finding maximum boundaries), using the standard mid = (left + right) >> 1
can cause an infinite loop. Consider when left = 4
and right = 5
:
mid = (4 + 5) >> 1 = 4
- If a black pixel is found at position 4, we set
left = mid = 4
- The loop continues with the same
left = 4, right = 5
, creating an infinite loop
The Solution:
Use mid = (left + right + 1) >> 1
when finding maximum boundaries. This ensures:
- When
left = 4
andright = 5
:mid = (4 + 5 + 1) >> 1 = 5
- The search space properly reduces in each iteration
2. Off-by-One Error in Area Calculation
The Pitfall:
Calculating the area as (bottom - top) * (right - left)
instead of (bottom - top + 1) * (right - left + 1)
. This mistake comes from forgetting that both boundary positions are inclusive.
Example: If top = 2, bottom = 4, left = 1, right = 3:
- Wrong:
(4 - 2) * (3 - 1) = 2 * 2 = 4
- Correct:
(4 - 2 + 1) * (3 - 1 + 1) = 3 * 3 = 9
3. Incorrect Binary Search Boundary Updates
The Pitfall:
Mixing up when to use right = mid
vs right = mid - 1
and left = mid
vs left = mid + 1
. The wrong choice can either skip valid positions or cause infinite loops.
The Solution: Remember the pattern:
- When finding minimum (top/left boundaries):
- If target found at mid:
right = mid
(mid could be the answer) - If not found:
left = mid + 1
(answer is strictly greater than mid)
- If target found at mid:
- When finding maximum (bottom/right boundaries):
- If target found at mid:
left = mid
(mid could be the answer) - If not found:
right = mid - 1
(answer is strictly less than mid)
- If target found at mid:
4. Not Leveraging the Known Black Pixel Position
The Pitfall:
Setting initial search ranges as [0, rows-1]
and [0, cols-1]
for all boundaries, which wastes the given information that position (x, y)
contains a black pixel.
The Solution: Use the known position to optimize search ranges:
- Top boundary: search in
[0, x]
(answer must be ≤ x) - Bottom boundary: search in
[x, rows-1]
(answer must be ≥ x) - Left boundary: search in
[0, y]
(answer must be ≤ y) - Right boundary: search in
[y, cols-1]
(answer must be ≥ y)
This reduces the search space and improves efficiency.
In a binary min heap, the minimum element can be found in:
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Want a Structured Path to Master System Design Too? Don’t Miss This!