Search a matrix sorted along rows and columns
An m × n matrix has every row sorted in increasing order from left to right and every
column sorted in increasing order from top to bottom. Rows may overlap in value. Return true if
target is in the matrix and false otherwise.
Examples
| Input | Output | Why |
|---|---|---|
matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5 | true | |
the same matrix, target = 20 | false |
Constraints
- 1 ≤ m, n ≤ 300
- −10⁹ ≤ matrix[i][j], target ≤ 10⁹
Hints
Hint 1
Reading order is not sorted here, so the previous problem's trick fails. Which cell has the useful property that one comparison discards something whole?
Hint 2
The top-right cell is the largest in its row and the smallest in its column.
Hint 3
If it is larger than target, its whole column is too large: move left. If smaller, its whole row is too small: move down.
Where people lose marks
- Starting at the top-left corner, which is the smallest in both its row and its column: a comparison there rules out nothing.
- Flattening the matrix and binary searching it as in the previous problem: reading order is not sorted here.
- Moving the wrong way after a comparison; say out loud which line the comparison eliminates.
Dry run on [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]],14
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 7row0col4
Start at the top-right corner, 15: the largest in its row, the smallest in its column.
-
Step 2 / 7row0col3
15 > 14: everything below 15 in its column is larger still. Discard the column, move left.
-
Step 3 / 7row1col3
11 < 14: everything left of 11 in its row is smaller still. Discard the row, move down.
-
Step 4 / 7row2col3
12 < 14: everything left of 12 in its row is smaller still. Discard the row, move down.
-
Step 5 / 7row2col2
16 > 14: everything below 16 in its column is larger still. Discard the column, move left.
-
Step 6 / 7row3col2
9 < 14: everything left of 9 in its row is smaller still. Discard the row, move down.
-
Step 7 / 7row3col2
14 = 14: found.
How to think about it
Binary search on each row works, at O(m log n), but uses only half of the structure. The top-right corner uses all of it. Its value is the largest in its row and the smallest in its column, so one comparison with the target settles a whole line:
- If the corner equals the target, done.
- If the corner is larger, every cell below it in its column is larger still: discard the column and move left.
- If the corner is smaller, every cell left of it in its row is smaller still: discard the row and move down.
The remaining cells always form a smaller matrix with its own top-right corner where we now stand. Each step
removes a row or a column, so there are at most m + n − 1 steps.
This is a search that discards part of the input with each comparison, like binary search, but it discards a line rather than half. For a square matrix it is the best possible: the target could sit on any cell of the anti-diagonal, those cells are in no order among themselves, and each comparison checks at most one of them. For a very flat matrix, m much smaller than n, a binary search in each row, O(m log n), is faster.
Complexity. Time O(m + n) · Space O(1)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def search_matrix_ii(matrix, target):
r, c = 0, len(matrix[0]) - 1 # top-right corner
while r < len(matrix) and c >= 0:
v = matrix[r][c]
if v == target:
return True
if v > target:
c -= 1 # the whole column below is larger
else:
r += 1 # the whole row to the left is smaller
return False#include <vector>
bool solve(const std::vector<std::vector<int>>& matrix, int target) {
int r = 0, c = (int)matrix[0].size() - 1; // top-right corner
while (r < (int)matrix.size() && c >= 0) {
int v = matrix[r][c];
if (v == target) return true;
if (v > target) --c; // the column below is larger
else ++r; // the row to the left is smaller
}
return false;
}class Solution {
public boolean searchMatrixII(int[][] matrix, int target) {
int r = 0, c = matrix[0].length - 1; // top-right corner
while (r < matrix.length && c >= 0) {
int v = matrix[r][c];
if (v == target) return true;
if (v > target) c--; // the column below is larger
else r++; // the row to the left is smaller
}
return false;
}
}function searchMatrixII(matrix, target) {
let r = 0, c = matrix[0].length - 1; // top-right corner
while (r < matrix.length && c >= 0) {
const v = matrix[r][c];
if (v === target) return true;
if (v > target) c--; // the column below is larger
else r++; // the row to the left is smaller
}
return false;
}fn solve<const C: usize>(matrix: &[[i64; C]], target: i64) -> bool {
// c counts columns still in play, so it never needs to go below zero
let (mut r, mut c) = (0usize, C);
while r < matrix.len() && c > 0 {
let v = matrix[r][c - 1]; // top-right corner of what is left
if v == target {
return true;
}
if v > target { c -= 1 } else { r += 1 }
}
false
}import Data.Array
solve :: [[Int]] -> Int -> Bool
solve rows target = go 0 (n - 1)
where
m = length rows
n = length (head rows)
a = listArray ((0, 0), (m - 1, n - 1)) (concat rows)
-- (r, c) is the top-right corner of the cells still in play
go r c
| r >= m || c < 0 = False
| v == target = True
| v > target = go r (c - 1) -- the column below is larger
| otherwise = go (r + 1) c -- the row to the left is smaller
where v = a ! (r, c)
Variants to try
- Count the cells ≤ x with the same walk in O(m + n). That count drives the k-th smallest search in the last module.
- Start from the bottom-left corner instead: the mirror image, with the moves up and right.
Next problems
Search a matrix sorted in reading order
Read row by row, the matrix is one sorted list of m × n values. Search indices 0 to mn − 1 and turn each into a row and a column.
Open the problem → Easy10 minFind a value in a sorted array
The search every other binary search is built on: halve a closed range until the value is found or the range is empty.
Open the problem → Easy10 minInteger square root without a library
Search the candidates 0 to n for the last x with x² ≤ n, and keep the square from overflowing.
Open the problem →Write a solution and run it against the real test table.
Keep going
Search a matrix sorted in reading order
Read row by row, the matrix is one sorted list of m × n values. Search indices 0 to mn − 1 and turn each into a row and a column.
Open the problem → Easy10 minFind a value in a sorted array
The search every other binary search is built on: halve a closed range until the value is found or the range is empty.
Open the problem → Easy10 minInteger square root without a library
Search the candidates 0 to n for the last x with x² ≤ n, and keep the square from overflowing.
Open the problem → Binary searchThe pattern behind it
Search the answer, not the array: halve a monotone predicate until one candidate is left.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.