74. Search a 2D Matrix

The matrix rows are sorted left-to-right and each row starts after the last row ends — so the whole grid is one big sorted sequence. Treat it as a flat array of length m * n and run a single binary search; map the flat midpoint back to (mid / n, mid % n) to read the real cell.

MediumBinary SearchMatrix / 2D ArrayTypeScript

PROBLEM What we're solving

Given an m × n integer matrix where each row is sorted left-to-right and the first integer of each row is greater than the last integer of the previous row, decide whether target appears in the matrix. Return true or false.

Concrete example. Matrix:

[[ 1,  3,  5,  7],
 [10, 11, 16, 20],
 [23, 30, 34, 60]]

Target = 3true (row 0, col 1). Target = 13 false.

KEY IDEA The matrix is one big sorted array in disguise

Insight → Because every row is sorted and each row starts strictly after the previous row ends, reading the grid left-to-right, top-to-bottom produces a single strictly-increasing sequence of m * n numbers. That means we can binary-search on flat index 0 to m * n − 1 and convert any midpoint mid to a real cell with row = ⌊mid / n⌋, col = mid % n. One pass, no 2D logic.

RECIPE Binary search on the virtual flat array

  • 0 · Set up bounds. lo = 0, hi = m * n − 1. These are flat indices into the conceptual 1D array.
  • 1 · Compute mid. mid = (lo + hi) >>> 1. Use unsigned right-shift to safely halve without overflow.
  • 2 · Map mid to a cell. row = ⌊mid / n⌋, col = mid % n. Division gives the row, remainder gives the column.
  • 3 · Three-way comparison. If matrix[row][col] === target → return true. If less than target → move lo = mid + 1 (target is to the right). If greater → move hi = mid − 1 (target is to the left).
  • 4 · Exhausted ⇒ absent. When lo > hi, target is not present — return false.
Classic confusion → using mid / n without Math.floor (or integer division) produces a fractional row index and silently reads undefined. Always floor the division. Likewise, forgetting the % n for the column and reading the entire row instead is a common copy-paste bug.

COST Complexity & alternatives

Linear scan every cell
O(m · n)
Ignores the sorted structure entirely.
Flat binary search
O(log m·n)
O(1) space; a single search over all m·n cells.

Space note

Only lo, hi, and mid are needed — constant extra space. No copy of the matrix is made.

Staircase search alternative.LeetCode 240 relaxes the constraint (rows independently sorted, but first element not necessarily greater than previous row's last). For that problem the standard trick is to start at the top-right corner and walk left or down. For this problem (LC 74), the flat binary search is strictly better at O(log m·n).

Pattern transfer →any time a 2D structure has a strict global order (row-sorted or column-sorted), ask "can I binary search a flat index?" — see also Kth Smallest Element in a Sorted Matrix (binary search on value range), Find Peak Element II, and Search in Rotated Sorted Array (similar three-way split on a conceptually flat sequence).

RUN IT Flatten → binary search on flat index → map back to (row, col)

step 0 / 8
STARTFlatten conceptually: treat the 3×4 matrix as a sorted array of length 12. Binary search on indices 0–11.
1function searchMatrix(matrix: number[][], target: number): boolean {
2 const m = matrix.length;
3 const n = matrix[0].length;
4 let lo = 0;
5 let hi = m * n - 1;
6
7 while (lo <= hi) {
8 const mid = (lo + hi) >>> 1; // integer midpoint
9 const val = matrix[Math.floor(mid / n)][mid % n]; // map flat → 2D
10
11 if (val === target) return true;
12 if (val < target) lo = mid + 1; // search right
13 else hi = mid - 1; // search left
14 }
15 return false;
16}
0
1
2
3
0
1
3
5
7
1
10
11
16
20
2
23
30
34
60
lo = 0
hi = 11
target = 3
Searching…
active search windowmid cell under inspectionfound (result)
slowfast

TYPESCRIPT The solution, annotated

searchMatrix.ts
function searchMatrix(matrix: number[][], target: number): boolean {
  const m = matrix.length;
  const n = matrix[0].length;
  let lo = 0;
  let hi = m * n - 1;

  while (lo <= hi) {
    const mid = (lo + hi) >>> 1;          // integer midpoint
    const val = matrix[Math.floor(mid / n)][mid % n];  // map flat → 2D

    if (val === target) return true;
    if (val < target)  lo = mid + 1;       // search right
    else               hi = mid - 1;       // search left
  }
  return false;
}

Reading it block by block

Lines 2–4 — initialise the search window. Read m and n once, then set lo = 0 and hi = m * n - 1. These are flat indices into the virtual 1D array the matrix secretly is.
Line 7 — unsigned right-shift for midpoint. (lo + hi) >>> 1 is JavaScript's idiomatic way to compute ⌊(lo + hi) / 2⌋ without integer overflow — safer than (lo + hi) / 2 | 0 when values are large.
Line 8 — the key mapping. Math.floor(mid / n) is the row (how many full rows fit before mid) and mid % n is the column (remainder within the row). This is the entire trick — everything else is plain binary search.
Lines 10–12 — three-way branch. Exact match ⇒ done. Value too small ⇒ advance lo past mid. Value too large ⇒ pull hi below mid. The loop exits when lo > hi.
Line 14 — not found. Falling through the loop guarantees the target is absent in the matrix. Return false.
Complexity → O(log(m · n)) time — each iteration halves a search space of m · n elements. O(1) space — only three scalar variables, no auxiliary data structure.

INTERVIEWFollow-ups they'll ask

  • "What if rows are individually sorted but NOT globally sorted?" (LC 240) You can no longer flatten. Use the top-right staircase walk: O(m + n) — still better than O(m · n).
  • "Return the index/position instead of a boolean?" Track mid at the moment val === target and decode it with [Math.floor(mid / n), mid % n].
  • "Count how many elements equal target?"The matrix contains distinct integers (per the constraint), so the count is 0 or 1. If duplicates were allowed you'd binary-search for lower and upper bounds.
  • "What if the matrix is empty?" Guard with if (!matrix.length || !matrix[0].length) return false before touching n.
  • "Binary search on value range vs index?"For Kth Smallest in Sorted Matrix the binary search range is the value space (min to max), not an index. Know which dimension you're searching.

OPTIMAL Binary Search

searchMatrix.ts
function searchMatrix(matrix: number[][], target: number): boolean {
  const m = matrix.length;
  const n = matrix[0].length;
  let lo = 0;
  let hi = m * n - 1;

  while (lo <= hi) {
    const mid = (lo + hi) >>> 1;          // integer midpoint
    const val = matrix[Math.floor(mid / n)][mid % n];  // map flat → 2D

    if (val === target) return true;
    if (val < target)  lo = mid + 1;       // search right
    else               hi = mid - 1;       // search left
  }
  return false;
}
Complexity → O(log(m · n)) time — each iteration halves a search space of m · n elements. O(1) space — only three scalar variables, no auxiliary data structure.

ALT 1 Brute force — scan every cell

O(m·n) time · O(1) space

Ignore the sorted structure entirely: walk all m·n cells and return true the moment one equals target.

approach-2.ts
function searchMatrix(matrix: number[][], target: number): boolean {
  for (const row of matrix) {
    for (const val of row) {
      if (val === target) return true;
    }
  }
  return false;
}
Note → Always correct, but it visits every element — O(m·n). Because the grid reads as one big sorted sequence, binary search over the flat index range finds the target in O(log(m·n)) instead.

MNEMONIC The one-liner

"Flatten with math, not memory — mid/n is the row, mid%n is the column."

TRIGGERS When you see ___ → reach for ___

sorted matrix where each row follows the lastflat-index binary search: lo=0, hi=m*n-1
map flat index to 2D cellrow = ⌊mid / n⌋, col = mid % n
globally sorted 2D grid, search for valuesingle O(log m·n) binary search
rows sorted independently but NOT globally chainedstaircase search from top-right, O(m+n)

SKELETON The reusable shape

skeleton.ts
function searchMatrix(matrix: number[][], target: number): boolean {
  const m = matrix.length, n = matrix[0].length;
  let lo = 0, hi = m * n - 1;
  while (lo <= hi) {
    const mid = (lo + hi) >>> 1;
    const val = matrix[Math.floor(mid / n)][mid % n];
    if (val === target) return true;
    if (val < target) lo = mid + 1;
    else hi = mid - 1;
  }
  return false;
}

FLASHCARDS Tap to flip

What property lets us treat the matrix as a flat sorted array?
Each row is sorted AND the first element of each row is greater than the last element of the previous row — so reading left→right, top→bottom gives a strictly increasing sequence.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 9
QUESTION 01
For a 3×4 matrix (m=3, n=4), what is the flat index of cell [2][1]?
QUESTION 02
What is the time complexity of the flat binary search on an m×n matrix?
QUESTION 03
Given matrix [[1,3,5,7],[10,11,16,20],[23,30,34,60]] and target=3, trace the first mid. With lo=0, hi=11, mid=?
QUESTION 04
In JavaScript/TypeScript, why prefer (lo + hi) >>> 1 over Math.floor((lo + hi) / 2)?
QUESTION 05
Which matrix property allows the flat binary search but is NOT present in LC 240 (Search a 2D Matrix II)?
QUESTION 06
A bug: const val = matrix[mid / n][mid % n] sometimes returns undefined. What is the fix?
QUESTION 07
matrix = [[1]], target = 0. What does the algorithm return?
QUESTION 08
#74 · Search a 2D MatrixTreat the m×n row-sorted matrix as one flattened sorted array of length m×n; map the midpoint index to (mid÷n, mid%n) and apply standard binary search in O(log(m×n)).Which algorithmic approach does this primarily use?
QUESTION 09
#74 · Search a 2D MatrixTreat the m×n row-sorted matrix as one flattened sorted array of length m×n; map the midpoint index to (mid÷n, mid%n) and apply standard binary search in O(log(m×n)).Which implementation correctly solves it?