On this page
Description
Matrix binary search finds a value in a sorted grid without checking every cell. Which method works depends on how the grid is sorted. If it reads as one sorted sequence, row after row, binary search it like a flat array and convert each index to a row and a column. If each row and each column is only sorted on its own, walk a staircase from a corner, ruling out a whole row or column at every step.
We'll go from simple to more technical:
- The pattern: binary search over a flat index.
- A second flavor: staircase search from the top-right corner.
- Where virtual indexes and two-way-sorted structures show up in production.
- Matching the method to the guarantee the matrix actually gives.
The approach
Checking every cell takes O(rows x cols). But suppose each row starts where the row above ended, so the whole grid reads as one sorted list. Then number the cells 0, 1, 2, and so on, row by row, and binary search those numbers. Cell number i is at row i / cols and column i % cols. You never build the flat list; you only convert numbers.
Searching for 20 in a grid with 4 columns:
| Column 0 | Column 1 | Column 2 | Column 3 | |
|---|---|---|---|---|
| Row 0 | 2 | 5 | 8 | 11 |
| Row 1 | 14 | 17 | 20 | 23 |
| Row 2 | 26 | 29 | 32 | 35 |
| lo | hi | mid | Cell (mid / 4, mid % 4) | Value | Move |
|---|---|---|---|---|---|
| 0 | 11 | 5 | (1, 1) | 17 | too small: lo = 6 |
| 6 | 11 | 8 | (2, 0) | 26 | too big: hi = 7 |
| 6 | 7 | 6 | (1, 2) | 20 | found |
Java
int lo = 0, hi = rows * cols - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int value = matrix[mid / cols][mid % cols]; // flat index -> (row, col)
if (value == target) return true;
if (value < target) lo = mid + 1;
else hi = mid - 1;
}
return false;
Python
def search(matrix: list[list[int]], target: int) -> bool:
rows, cols = len(matrix), len(matrix[0])
lo, hi = 0, rows * cols - 1
while lo <= hi:
mid = (lo + hi) // 2
value = matrix[mid // cols][mid % cols] # flat index -> (row, col)
if value == target:
return True
if value < target:
lo = mid + 1
else:
hi = mid - 1
return False
A second flavor: staircase search from a corner
A weaker guarantee is more common. Each row is sorted left to right and each column top to bottom. But a row may start with a smaller value than the row above ended with. Flat binary search fails here, because the grid no longer reads as one sorted list. Start at the top-right corner instead. Moving left makes values smaller, and moving down makes them bigger. So one comparison tells you which way to go, and each step rules out a whole row or column.
Searching for 11 in this grid, where row 0 ends at 14 but row 1 starts at 5:
| Column 0 | Column 1 | Column 2 | Column 3 | |
|---|---|---|---|---|
| Row 0 | 3 | 6 | 10 | 14 |
| Row 1 | 5 | 9 | 12 | 18 |
| Row 2 | 8 | 11 | 15 | 21 |
The walk visits 14 (too big: go left), 10 (too small: go down), 12 (too big: left), 9 (too small: down), then finds 11.
Java
int row = 0, col = cols - 1; // top-right corner
while (row < rows && col >= 0) {
int value = matrix[row][col];
if (value == target) return true;
if (value > target) col--; // eliminate this whole column
else row++; // eliminate this whole row
}
return false;
Python
def search(matrix: list[list[int]], target: int) -> bool:
rows, cols = len(matrix), len(matrix[0])
row, col = 0, cols - 1 # top-right corner
while row < rows and col >= 0:
value = matrix[row][col]
if value == target:
return True
if value > target:
col -= 1 # eliminate this whole column
else:
row += 1 # eliminate this whole row
return False
The corner matters. From the top-left, moving down and moving right both make values bigger. No comparison can tell you which way is safe, so the walk doesn't work from there.
Where you'll see it
A sorted sequence is often stored in pieces: pages of results, or fixed-size blocks on disk. You can still binary search it by turning a position in the sequence into "which block, and where in it", the same conversion as row and column. That avoids building a merged copy first.
Which tool for which job?
| You need | Use |
|---|---|
| Search a matrix that's sorted as one sequence | binary search on a flat index: (i / cols, i % cols) |
| Search when rows and columns are sorted separately | staircase from the top-right (or bottom-left) |
| Very few rows, very long rows | binary search each row, O(rows log cols) |
| Count values ≤ x | a staircase walk, O(rows + cols) |
| The k-th smallest | binary search on the value, counting with the staircase |
| Bisect in Python without copying | a small class with __len__ and __getitem__ |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Flat binary search on a row-and-column-sorted matrix | misses values that are there | staircase search |
| Staircase from the top-left or bottom-right | both moves increase (or decrease) - no safe step | start top-right or bottom-left |
Dividing the flat index by rows | the wrong cell | divide and take the remainder by cols |
rows * cols as a Java int | overflow for big grids | compute the size in long |
| Flattening into a copy first | O(rows x cols) time and memory | index the matrix directly |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. Each row and each column is sorted, but row 2's first value is smaller than row 1's last. Can you use flat binary search?
Show answer
No. Flat binary search needs the whole grid to read as one sorted list, row after row, and this grid doesn't. It's a different problem: use the staircase walk, which only needs each row and column sorted on its own.
2. Why does the staircase start at the top-right corner rather than the top-left?
Show answer
At the top-right, moving left makes values smaller and moving down makes them bigger. Comparing with the target tells you which way to go, and so which row or column to rule out. At the top-left, both moves make values bigger, so no comparison gives a safe step.
3. A grid has 2 rows and 3 columns. Which cell is flat index 4?
Show answer
Row 4 / 3 = 1, column 4 % 3 = 1, so matrix[1][1]. Divide and take the remainder by the number of columns, not rows.
Key takeaways
- A grid that reads as one sorted list can be binary searched through a flat index: row
i / cols, columni % cols. That's O(log(rows x cols)). - A grid sorted only by row and by column needs the staircase walk, O(rows + cols).
- Start the staircase at the top-right or bottom-left, where the two moves change the value in opposite directions.
- Check which sort guarantee the grid really has before choosing a method.