Each row is sorted left-to-right and each column top-to-bottom, but the grid is notone flattened sorted array — so the LC 74 binary-search trick fails. Stand at the top-right corner instead: if the cell is too big move left, if too small move down. Each step kills a whole row or column in O(m + n).
Given an m × n matrix where every row is sorted left-to-right and every column is sorted top-to-bottom, decide whether target appears. Return true or false.
Concrete example. 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 (row 1, col 1). Target = 20 → false.
O(m + n).r = 0, c = cols − 1. This is the only cell that is the max of its row and the min of its column.val = matrix[r][c].val === target return true.val > target, every cell below in this column is even bigger, so the whole column is useless — c−−.val < target, every cell to the left in this row is even smaller, so the whole row is useless — r++.r falls off the bottom or c off the left, return false.m·nrange like LC 74. The first element of a row can be smaller than the last element of the previous row (here row 3 starts at 10 while row 2 ends at 22). Reaching for the flat binary search here gives wrong answers. Use the staircase walk.Binary-searching each of the m rows is O(m log n) — correct, but worse than the staircase walk when m and n are comparable, and it throws away the column ordering. The staircase exploits both sort directions at once for a clean linear-in-the-perimeter bound.
[0,4]. Everything below it is larger; everything to its left is smaller — so each comparison can discard a whole row or column.1function searchMatrix(matrix: number[][], target: number): boolean {2 const rows = matrix.length;3 const cols = matrix[0].length;45▶ // Start at the TOP-RIGHT corner.6▶ let r = 0;7▶ let c = cols - 1;89 while (r < rows && c >= 0) {10 const val = matrix[r][c];11 if (val === target) return true;12 if (val > target) c--; // too big: drop the column, move left13 else r++; // too small: drop the row, move down14 }15 return false;16}
function searchMatrix(matrix: number[][], target: number): boolean {
const rows = matrix.length;
const cols = matrix[0].length;
// Start at the TOP-RIGHT corner.
let r = 0;
let c = cols - 1;
while (r < rows && c >= 0) {
const val = matrix[r][c];
if (val === target) return true;
if (val > target) c--; // too big: drop the column, move left
else r++; // too small: drop the row, move down
}
return false;
}rows and colsbound the walk. (In production you'd guard an empty matrix first.)r = 0, c = cols - 1. This corner is the unique cell that is simultaneously the largest in its row and the smallest in its column, which is what makes the next comparison decisive.r is above the bottom and c is right of the left edge. Falling off either side means the target is absent.matrix[r][c] === targetwe're done — return true.val > target means every cell beneath it in column c is also too big, so the column is dead — decrement c.val < target means every cell to the left in row r is also too small, so the row is dead — increment r.target; return false.m + n steps before walking off the grid. O(1) space — only two index variables.[r, c] at the match instead of true, and a sentinel like [-1, -1] on exhaustion.O(m log n) vs O(m + n); the staircase wins when the matrix is roughly square and uses both sort axes.function searchMatrix(matrix: number[][], target: number): boolean {
const rows = matrix.length;
const cols = matrix[0].length;
// Start at the TOP-RIGHT corner.
let r = 0;
let c = cols - 1;
while (r < rows && c >= 0) {
const val = matrix[r][c];
if (val === target) return true;
if (val > target) c--; // too big: drop the column, move left
else r++; // too small: drop the row, move down
}
return false;
}m + n steps before walking off the grid. O(1) space — only two index variables.Ignore the sorted structure and check all m·n cells, returning true on the first match.
function searchMatrix(matrix: number[][], target: number): boolean {
for (const row of matrix) {
for (const val of row) {
if (val === target) return true;
}
}
return false;
}O(m + n).Each row is individually sorted, so binary-search the target within every row.
function searchMatrix(matrix: number[][], target: number): boolean {
const cols = matrix[0].length;
for (const row of matrix) {
let lo = 0;
let hi = cols - 1;
while (lo <= hi) {
const mid = (lo + hi) >>> 1;
if (row[mid] === target) return true;
if (row[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
}
return false;
}O(m log n) — worse than the O(m + n) staircase for roughly square matrices.| matrix sorted by row AND by column, but not globally | top-right staircase walk, O(m+n) |
| one comparison should kill a whole row or column | start at a corner where directions disagree |
| current cell > target | move left (c--): drop the column |
| current cell < target | move down (r++): drop the row |
function searchMatrix(matrix: number[][], target: number): boolean {
let r = 0, c = matrix[0].length - 1; // top-right
while (r < matrix.length && c >= 0) {
const v = matrix[r][c];
if (v === target) return true;
if (v > target) c--; // move left
else r++; // move down
}
return false;
}target = 20, the walk eventually returns: