1428. Leftmost Column with at Least a One 🔒
Problem Description
You are given a binary matrix where each element is either 0
or 1
. The matrix has a special property: each row is sorted in non-decreasing order (meaning all 0
s come before all 1
s in each row).
Your task is to find the index of the leftmost column that contains at least one 1
. If no column contains a 1
, return -1
.
The key constraint is that you cannot access the matrix directly. Instead, you must use a BinaryMatrix
interface that provides two methods:
BinaryMatrix.get(row, col)
- returns the element at position(row, col)
BinaryMatrix.dimensions()
- returns[rows, cols]
representing the matrix dimensions
You are limited to making at most 1000
calls to BinaryMatrix.get()
.
For example, consider this matrix:
[[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1]]
The leftmost column containing a 1
is column index 1
(the second column), so the answer would be 1
.
The solution uses binary search on each row to efficiently find where the first 1
appears in that row. By checking all rows and taking the minimum column index where a 1
is found, we can determine the overall leftmost column with a 1
. The bisect_left
function performs binary search to find the insertion point for value 1
in each sorted row, which gives us the index of the first 1
(or the length of the row if no 1
exists).
Intuition
Since each row is sorted with all 0
s before all 1
s, we can think of each row as having a "transition point" where it changes from 0
to 1
. Our goal is to find which row has the earliest (leftmost) transition point across the entire matrix.
The naive approach would be to check every element in the matrix starting from the leftmost column and moving right until we find a 1
. However, this could require checking many elements, potentially up to m × n
in the worst case.
The key insight is that because each row is sorted, we can use binary search within each row to quickly locate where the first 1
appears. Binary search allows us to find this transition point in O(log n)
time per row instead of O(n)
time.
Think of it this way: for each row, we're asking "at what column index does the first 1
appear?" Binary search is perfect for this because:
- We're looking for a specific boundary (the change from
0
to1
) - The row is sorted, so all
0
s are on the left and all1
s are on the right - We can check any position and know whether we need to look further left (if we see a
1
) or further right (if we see a0
)
By applying binary search to each row independently and keeping track of the minimum column index where we find a 1
, we get the overall leftmost column containing a 1
. The total number of get()
calls becomes O(m × log n)
, which is much more efficient than a linear search and stays well under the 1000-call limit for reasonable matrix sizes.
The use of bisect_left
with a key function elegantly handles the binary search - it finds the leftmost position where 1
would be inserted to maintain sorted order, which is exactly the index of the first 1
in the row (or the row length if no 1
exists).
Learn more about Binary Search patterns.
Solution Approach
The implementation uses binary search to efficiently find the leftmost column containing a 1
.
First, we get the matrix dimensions by calling binaryMatrix.dimensions()
, which returns [m, n]
where m
is the number of rows and n
is the number of columns.
We initialize a variable ans
to n
(the total number of columns). This will track the minimum column index where we've found a 1
so far. Starting with n
ensures that if no 1
is found in any row, we can detect this condition at the end.
For each row i
from 0
to m-1
, we perform a binary search to find the column index of the first 1
in that row:
- We use Python's
bisect_left
function with a custom key function bisect_left(range(n), 1, key=lambda k: binaryMatrix.get(i, k))
searches for the leftmost position where value1
would fit in the sorted sequence- The
range(n)
represents column indices from0
ton-1
- The key function
lambda k: binaryMatrix.get(i, k)
tellsbisect_left
to use the actual matrix values for comparison - This returns the column index
j
where the first1
appears in rowi
(orn
if the row contains only0
s)
After finding the position j
for each row, we update ans
with min(ans, j)
to keep track of the overall leftmost column containing a 1
.
Finally, we check if ans >= n
. If true, it means no 1
was found in any row (all rows returned j = n
), so we return -1
. Otherwise, we return ans
as the leftmost column index containing a 1
.
The time complexity is O(m × log n)
where we perform binary search (O(log n)
) for each of the m
rows. The space complexity is O(1)
as we only use a constant amount of extra space.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this binary matrix:
[[0, 0, 1], [0, 1, 1], [0, 0, 0]]
Step 1: Initialize
- Get dimensions:
m = 3
rows,n = 3
columns - Set
ans = 3
(starting with the number of columns)
Step 2: Process Row 0 [0, 0, 1]
- Use binary search to find where the first
1
appears - Binary search process:
- Check middle column (col 1): value is
0
, so first1
must be to the right - Check between cols 2-2: value at col 2 is
1
- First
1
is at column index2
- Check middle column (col 1): value is
- Update
ans = min(3, 2) = 2
Step 3: Process Row 1 [0, 1, 1]
- Binary search to find first
1
:- Check middle column (col 1): value is
1
, so this could be the first1
or there might be one to the left - Check col 0: value is
0
- First
1
is at column index1
- Check middle column (col 1): value is
- Update
ans = min(2, 1) = 1
Step 4: Process Row 2 [0, 0, 0]
- Binary search to find first
1
:- Check middle column (col 1): value is
0
, so look right - Check col 2: value is
0
- No
1
found in this row, returns index3
(equal ton
)
- Check middle column (col 1): value is
- Update
ans = min(1, 3) = 1
Step 5: Return Result
- Since
ans = 1 < n = 3
, we found at least one1
- Return
1
as the leftmost column containing a1
The answer is correct: column index 1 is indeed the leftmost column with a 1
(from row 1).
Solution Implementation
1# """
2# This is BinaryMatrix's API interface.
3# You should not implement it, or speculate about its implementation
4# """
5# class BinaryMatrix(object):
6# def get(self, row: int, col: int) -> int:
7# def dimensions(self) -> List[int]:
8
9from bisect import bisect_left
10from typing import List
11
12
13class Solution:
14 def leftMostColumnWithOne(self, binaryMatrix: "BinaryMatrix") -> int:
15 # Get the dimensions of the binary matrix (rows x columns)
16 rows, cols = binaryMatrix.dimensions()
17
18 # Initialize the answer to the total number of columns
19 # This represents the rightmost possible position + 1
20 leftmost_col = cols
21
22 # Iterate through each row to find the leftmost column with a 1
23 for row_idx in range(rows):
24 # Use binary search to find the first occurrence of 1 in the current row
25 # bisect_left finds the leftmost position where 1 should be inserted
26 # to maintain sorted order, which is the index of the first 1
27 first_one_col = bisect_left(
28 range(cols),
29 1,
30 key=lambda col_idx: binaryMatrix.get(row_idx, col_idx)
31 )
32
33 # Update the leftmost column if we found a 1 further to the left
34 leftmost_col = min(leftmost_col, first_one_col)
35
36 # If no 1 was found in any row, return -1
37 # Otherwise, return the leftmost column index containing a 1
38 return -1 if leftmost_col >= cols else leftmost_col
39
1/**
2 * // This is the BinaryMatrix's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * interface BinaryMatrix {
5 * public int get(int row, int col) {}
6 * public List<Integer> dimensions {}
7 * };
8 */
9
10class Solution {
11 /**
12 * Finds the leftmost column that contains at least one 1 in a binary matrix.
13 * The matrix rows are sorted in non-decreasing order (0s before 1s).
14 *
15 * @param binaryMatrix The binary matrix to search
16 * @return The index of the leftmost column with a 1, or -1 if no 1 exists
17 */
18 public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) {
19 // Get matrix dimensions
20 List<Integer> dimensions = binaryMatrix.dimensions();
21 int rows = dimensions.get(0);
22 int cols = dimensions.get(1);
23
24 // Initialize result to cols (invalid column index)
25 int leftmostColumn = cols;
26
27 // Process each row to find the leftmost 1
28 for (int row = 0; row < rows; row++) {
29 // Binary search for the first occurrence of 1 in current row
30 int left = 0;
31 int right = cols;
32
33 while (left < right) {
34 int mid = (left + right) / 2;
35
36 // If we found a 1, search left half including mid
37 if (binaryMatrix.get(row, mid) == 1) {
38 right = mid;
39 } else {
40 // If we found a 0, search right half excluding mid
41 left = mid + 1;
42 }
43 }
44
45 // Update the leftmost column if we found a 1 earlier in this row
46 leftmostColumn = Math.min(leftmostColumn, left);
47 }
48
49 // Return -1 if no 1 was found, otherwise return the leftmost column index
50 return leftmostColumn >= cols ? -1 : leftmostColumn;
51 }
52}
53
1/**
2 * // This is the BinaryMatrix's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * class BinaryMatrix {
5 * public:
6 * int get(int row, int col);
7 * vector<int> dimensions();
8 * };
9 */
10
11class Solution {
12public:
13 int leftMostColumnWithOne(BinaryMatrix& binaryMatrix) {
14 // Get matrix dimensions: dimensions[0] = rows, dimensions[1] = columns
15 vector<int> dimensions = binaryMatrix.dimensions();
16 int rowCount = dimensions[0];
17 int colCount = dimensions[1];
18
19 // Initialize result to column count (represents no 1 found initially)
20 int leftmostColumn = colCount;
21
22 // Iterate through each row to find the leftmost 1
23 for (int row = 0; row < rowCount; ++row) {
24 // Binary search for the first occurrence of 1 in current row
25 int left = 0;
26 int right = colCount;
27
28 while (left < right) {
29 int mid = (left + right) >> 1; // Equivalent to (left + right) / 2
30
31 // If we found a 1, search in the left half (including mid)
32 if (binaryMatrix.get(row, mid) == 1) {
33 right = mid;
34 }
35 // If we found a 0, search in the right half (excluding mid)
36 else {
37 left = mid + 1;
38 }
39 }
40
41 // Update the leftmost column if we found a 1 further to the left
42 leftmostColumn = min(leftmostColumn, left);
43 }
44
45 // Return -1 if no 1 was found, otherwise return the leftmost column index
46 return (leftmostColumn >= colCount) ? -1 : leftmostColumn;
47 }
48};
49
1/**
2 * // This is the BinaryMatrix's API interface.
3 * // You should not implement it, or speculate about its implementation
4 * class BinaryMatrix {
5 * get(row: number, col: number): number {}
6 *
7 * dimensions(): number[] {}
8 * }
9 */
10
11/**
12 * Finds the leftmost column index that contains at least one 1 in a binary matrix.
13 * Each row in the matrix is sorted in non-decreasing order (0s followed by 1s).
14 *
15 * @param binaryMatrix - A binary matrix with sorted rows
16 * @returns The index of the leftmost column containing a 1, or -1 if no such column exists
17 */
18function leftMostColumnWithOne(binaryMatrix: BinaryMatrix): number {
19 // Get matrix dimensions
20 const [rows, cols]: number[] = binaryMatrix.dimensions();
21
22 // Initialize result to number of columns (represents no column found yet)
23 let leftmostColumn: number = cols;
24
25 // Iterate through each row to find the leftmost 1
26 for (let row: number = 0; row < rows; row++) {
27 // Binary search for the first occurrence of 1 in current row
28 let left: number = 0;
29 let right: number = cols;
30
31 while (left < right) {
32 // Calculate middle index using bit shift for integer division
33 const mid: number = (left + right) >> 1;
34
35 // Check if current position contains 1
36 if (binaryMatrix.get(row, mid) === 1) {
37 // If 1 found, search left half including mid
38 right = mid;
39 } else {
40 // If 0 found, search right half excluding mid
41 left = mid + 1;
42 }
43 }
44
45 // Update the leftmost column if current row has 1 at an earlier position
46 leftmostColumn = Math.min(leftmostColumn, left);
47 }
48
49 // Return -1 if no 1 was found, otherwise return the leftmost column index
50 return leftmostColumn >= cols ? -1 : leftmostColumn;
51}
52
Time and Space Complexity
The time complexity is O(m × log n)
, where m
is the number of rows and n
is the number of columns in the binary matrix. The algorithm iterates through all m
rows, and for each row, it performs a binary search using bisect_left
to find the leftmost position of 1
in that row. Binary search has a time complexity of O(log n)
since it searches through n
columns. Therefore, the overall time complexity is O(m × log n)
.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space to store variables m
, n
, ans
, i
, and j
. The bisect_left
function with the range(n)
object and lambda function uses O(1)
space since range
objects in Python 3 are lazy iterators that don't create the entire list in memory, and the lambda function is evaluated on-demand without storing intermediate results.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Inefficient Linear Search Instead of Binary Search
A common mistake is scanning each row from left to right to find the first 1
, which results in O(m × n)
time complexity in the worst case.
Incorrect Approach:
def leftMostColumnWithOne(self, binaryMatrix: "BinaryMatrix") -> int:
rows, cols = binaryMatrix.dimensions()
leftmost_col = cols
for row_idx in range(rows):
# Linear search - inefficient!
for col_idx in range(cols):
if binaryMatrix.get(row_idx, col_idx) == 1:
leftmost_col = min(leftmost_col, col_idx)
break # Found first 1 in this row
return -1 if leftmost_col >= cols else leftmost_col
Solution: Use binary search as shown in the original solution to achieve O(m × log n)
complexity.
2. Starting Search from Previously Found Column (Staircase Approach)
While the staircase search pattern (starting from top-right and moving left/down) is actually optimal with O(m + n)
complexity, implementing it incorrectly can lead to bugs.
Potential Bug in Staircase Implementation:
def leftMostColumnWithOne(self, binaryMatrix: "BinaryMatrix") -> int:
rows, cols = binaryMatrix.dimensions()
row, col = 0, cols - 1
while row < rows and col >= 0:
if binaryMatrix.get(row, col) == 1:
col -= 1 # Move left
else:
row += 1 # Move down
# Bug: returning col directly when col might be -1
return col # Wrong! Should return col + 1 or handle -1 case
Correct Staircase Solution:
def leftMostColumnWithOne(self, binaryMatrix: "BinaryMatrix") -> int:
rows, cols = binaryMatrix.dimensions()
row, col = 0, cols - 1
while row < rows and col >= 0:
if binaryMatrix.get(row, col) == 1:
col -= 1
else:
row += 1
return col + 1 if col < cols - 1 else -1
3. Misunderstanding bisect_left Return Value
Some developers might incorrectly validate whether a 1
exists after using bisect_left
.
Incorrect Validation:
for row_idx in range(rows):
first_one_col = bisect_left(
range(cols),
1,
key=lambda col_idx: binaryMatrix.get(row_idx, col_idx)
)
# Unnecessary check - bisect_left already handles this!
if first_one_col < cols and binaryMatrix.get(row_idx, first_one_col) == 1:
leftmost_col = min(leftmost_col, first_one_col)
Why it's wrong: bisect_left
returns the correct insertion position. If the row contains only 0
s, it returns cols
(end of range). The additional check wastes API calls and adds unnecessary complexity.
4. Not Handling Edge Cases Properly
Forgetting to handle matrices with no 1
s or initializing the result variable incorrectly.
Incorrect Initialization:
leftmost_col = -1 # Wrong initialization!
for row_idx in range(rows):
first_one_col = bisect_left(...)
if first_one_col < cols:
if leftmost_col == -1:
leftmost_col = first_one_col
else:
leftmost_col = min(leftmost_col, first_one_col)
return leftmost_col
Solution: Initialize to cols
(as in the original solution) to naturally handle the case where no 1
s exist, avoiding special conditional logic.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
Recommended Readings
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
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!