DSA sheet · Binary Search · Search in a 2D matrix pattern

Search a 2D Matrix II

This is the second question of the 2D-matrix pattern, and it's a trap question. The matrix is sorted along every row and every column, so your hand reaches for binary search. The teacher shows why binary search does not work here, and then builds the staircase search: start at a corner where "bigger" and "smaller" point in different directions, and step left or down until you find the target. It runs in O(m + n). She says interviewers like this one because it shows whether you understood the previous problem or just memorised its code. She also explains which corner you can start from and why, which is a favourite follow-up question.

Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the conditions from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember

Part 0 · Before starting

Binary search in one paragraph

Binary search works when looking at one position lets you say "the answer is definitely not on this side". That's true for a sorted array, or any yes/no question whose answers go "no … no, yes … yes" in order. We keep low and high (the first and last positions still possible), look at the middle mid, and throw away half with low = mid + 1 or high = mid - 1. Each step halves the work, so it takes O(log n) steps.

mid = low + (high - low) // 2

In Java/C++, (low + high) can go past the largest int and wrap around to a negative number (overflow). low + (high - low) // 2 gives the same middle without ever making that big sum. Python ints never overflow, so in Python both forms are fine, but the safe form is a good habit.

Matrix words

What we learned in Problem 12 (LeetCode 74)

There, a third property was given: each row's first value is bigger than the previous row's last value. That made the whole matrix one sorted line when read row by row, so we could binary search the rows and then the row. Keep that in mind: this problem doesn't have that property.

Part A · Brute force: look at every cell

LeetCode 240 · Search a 2D Matrix II

1The question in simple words

Write an efficient function that tells whether target is in an m × n matrix. The matrix has two properties:

  1. Every row is sorted in ascending order, left to right.
  2. Every column is sorted in ascending order, top to bottom.
          c0  c1  c2  c3  c4
   r0  [   1   4   7  11  15 ]
   r1  [   2   5   8  12  19 ]
   r2  [   3   6   9  16  22 ]
   r3  [  10  13  14  17  24 ]
   r4  [  18  21  23  26  30 ]

Example 1: target 5 → True. Example 2: target 20 → False.

2What the constraints tell us

3Intuition

Ignore every property. Look at every cell, one by one. If one equals target, say True.

4Building the logic

There's nothing to derive: two loops over rows and columns. It's correct for any matrix, sorted or not. That's exactly its weakness: it doesn't use either sorted property.

5Approach steps

  1. For each row, for each value in the row: if it equals target → True.
  2. After the loops → False.

6Code (Python)

brute force, O(m·n)
class Solution:
    def searchMatrix(self, matrix, target):
        for row in matrix:
            for value in row:
                if value == target:
                    return True
        return False

7Code line by line

linewhat it means
for row in matrix:Go through the rows top to bottom.
for value in row:Go through each number of that row.
if value == target: return TrueFound it, stop.
return FalseChecked all m·n cells, not there.

8Dry run

Target 5: row 0 checks 1, 4, 7, 11, 15 (no). Row 1 checks 2, then 5 → True after 7 looks. Target 20: all 25 cells are checked → False.

9Complexity & remember

RememberPasses the judge (9 × 10⁴), but it throws away the sorted information. An interviewer will ask for better.

Part B · Why the binary search from Problem 12 breaks

1The question

Same problem. The matrix is sorted, and "sorted" makes us think of binary search. Can we reuse Problem 12's plan (binary search for the valid row, then binary search in it)?

2Constraints

Same as Part A. The thing to look at here is not the size but the properties: only two are given, and the third one from Problem 12 is missing.

3Intuition

Problem 12's row search worked only because each row started after the previous row ended. Comparing with one row's first value then told us about all the rows above and below. Without that guarantee, a row's first value tells us nothing about the rows above it.

Reason 1: comparing with a row's first value gives a wrong answer

Say target = 4, and we look at row 1, which starts with 2. In Problem 12, "4 > 2" would mean "4 is in this row or below". Here, that's false: 4 is in row 0, column 1, above row 1. Row 0 ends at 15, which is bigger than row 1's first value 2, so the rows overlap. A binary search that went down from row 1 would never find the 4.

          c0  c1  c2  c3  c4
   r0  [   1   4   7  11  15 ]    ← 4 is here, ABOVE row 1
   r1  [   2   5   8  12  19 ]    ← "4 > 2, so go down?"  wrong ✗

It doesn't matter whether you compare with the first or the last value of a row. Without the third property, neither one is safe.

Reason 2: "open it up" and see if it's sorted

The test from the last video: write the rows one after another and check whether the line is sorted.

row-wise1471115258…15 then 2 → not sorted
column-wise123101845…18 then 4 → not sorted

Opened up either way, it's not one sorted line. So there's no single sorted array to binary search, and the Problem 12 logic can't be used.

4So what's left?

Not linear (too slow for the interviewer), not binary search (doesn't apply). But the matrix is sorted, and we should still use that. The trick is to find a cell where one comparison tells us exactly one direction to move. That's Part C.

5–8 (nothing to code here)

This part is the "why not". There's no code to run, because the idea is wrong. Remember the two reasons in case an interviewer asks.

9Remember

RememberRows sorted + columns sorted is not enough for row-then-row binary search. You also need "each row starts after the previous ends". Quick test: open the matrix into one line. Not sorted → no plain binary search.

Part C · Staircase search from the top-right corner

1The question

Same: is target in the row-sorted and column-sorted matrix? Return True/False. Do better than O(m·n).

2What the constraints tell us

3Intuition: stand where "bigger" and "smaller" point different ways

Picture yourself standing on a cell and comparing it with the target. The value is either too small (you want something bigger) or too big (you want something smaller). The question is: from this cell, is there exactly one direction for "bigger" and exactly one for "smaller"?

Try the top-left cell (1)

Target 3. 1 < 3, so we want bigger. But right (4) is bigger and down (2) is bigger too. Two choices. Which one? From code, we can't tell. Stuck.

Try 4, then 7, then 11 (the top row)

Target 7 while standing on 4: we want bigger, but right and down are both bigger. Same problem for 7 and 11. (Don't think "I can see 7 is just to the right". Think like code: the code can't see, it can only compare the current cell.)

Try the top-right cell (15)

From 15: the only neighbours are left (11, smaller, because the row grows to the right) and down (19, bigger, because the column grows downward). So:

Exactly one path for each case. Now the code can decide. And this stays true as we walk: we only ever move left or down, so everything above us and to our right has already been ruled out.

4Building the conditions from examples

Example: target 4 → value too big → move left (column − 1)

At 15: 15 > 4. Going down only gives bigger numbers, so we must go left. The row doesn't change, the column goes down by one.

Rule: value > targetc -= 1 (go left)
Doubt: when 15 > 4 and we move left, are we sure 4 isn't somewhere below 15?
→ Yes. Column 4 is sorted top to bottom, and 15 is its top, the smallest value in that column. If even the smallest is bigger than 4, nothing in the whole column can be 4. So moving left throws away the entire column, not just one cell.

At 11 (still > 4) → left. At 7 (> 4) → left. At 4 → it's equal.

Rule: value == targetreturn True

Example: target 5 → value too small → move down (row + 1)

Same walk: 15 → 11 → 7 → 4. Now 4 < 5, so we want bigger. Left would only give smaller numbers (we just came from there and they were too big, and left of 4 is 1). So we go down. The column stays the same, the row increases. Down from 4 is 5 → equal → True.

Rule: value < targetr += 1 (go down)
Doubt: when we're at 4 (row 0) and move down, do we lose anything in row 0?
→ No. In row 0 we've only kept the part from column 0 up to our column 1, and 4 is the last, biggest part of that. If 4 is smaller than the target, everything left of it is even smaller. So moving down throws away the rest of the whole row.

Example: target 0 → walking off the edge

15 → 11 → 7 → 4 → 1 are all bigger than 0, so we keep going left. From 1 (column 0), c -= 1 makes c = -1. We've left the matrix. So the moves need a boundary check:

Put all the rules inside while r < m and c >= 0:. If the loop ends, we walked off the matrix without finding the target → return False.

Why if / elif / else and not three separate ifs?

The teacher points out that after c -= 1 makes c = −1, a separate if right below would run straight away, before the while gets a chance to check the boundary. With elif/else, exactly one branch runs per loop, and then the loop condition is checked again.

Doubt: in Java that separate if crashes. What happens in Python?
→ Something worse: no crash. matrix[r][-1] means "the last column" in Python, so the code would quietly read the wrong cell and could take a wrong step. Always use elif/else here. The last case can be a plain else, because if the value is not equal and not bigger, it must be smaller.

5Approach steps

  1. m = len(matrix), n = len(matrix[0]).
  2. Start at the top-right corner: r = 0, c = n - 1.
  3. While r < m and c >= 0: equal → True · bigger than target → c -= 1 (left) · else → r += 1 (down).
  4. Loop ended → False.

6Code (Python)

Search a 2D Matrix II: staircase from top-right
class Solution:
    def searchMatrix(self, matrix, target):
        m = len(matrix)
        n = len(matrix[0])
        r, c = 0, n - 1                  # top-right corner

        while r < m and c >= 0:          # still inside the matrix
            if matrix[r][c] == target:
                return True
            elif matrix[r][c] > target:
                c -= 1                   # too big: whole column is too big, go left
            else:
                r += 1                   # too small: rest of row is too small, go down
        return False

7Code line by line

linewhat it means
m = len(matrix) n = len(matrix[0])Number of rows and columns.
r, c = 0, n - 1Row 0 (top), last column (right). The only kinds of corner where bigger and smaller each have one direction (bottom-left also works, Part D).
while r < m and c >= 0:r only grows, so it can fall off the bottom. c only shrinks, so it can fall off the left. Either one means we're out.
if matrix[r][c] == target: return TrueFound.
elif matrix[r][c] > target: c -= 1This cell is the smallest of what's left in its column, and it's still too big → drop the column, step left.
else: r += 1This cell is the biggest of what's left in its row, and it's still too small → drop the row, step down.
return FalseWe walked off the grid: every row or every column has been ruled out.

8Dry run, step by step

Target 5 (her first example, found)

step 1: 15 > 5 → left
  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
step 2: 11 > 5 → left
  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
step 3: 7 > 5 → left
  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
step 4: 4 < 5 → down
  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
step 5: 5 == 5 → True ✓
  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

Grey = thrown away. Each "left" drops a whole column, each "down" drops a whole row. (Target 4 is the same walk, stopping at step 4 with 4 == 4.)

steprcmatrix[r][c]decisionwhat we throw away
1041515 > 5 → c = 3column 4
2031111 > 5 → c = 2column 3
30277 > 5 → c = 1column 2
40144 < 5 → r = 1row 0
5115equal → True-

Target 0 (not there, falls off the left edge)

steprcvaluedecision
1–404 → 115, 11, 7, 4all > 0 → keep going left
50011 > 0 → c = −1
end0−1-c >= 0 fails → loop stops → False

Target 18 (her long example: a real staircase)

          c0    c1    c2    c3    c4
   r0      1     4     7    11   [15]1
   r1      2     5     8   [12]3 [19]2
   r2      3     6     9   [16]4  22
   r3     10    13    14   [17]5  24
   r4   [18]9 [21]8 [23]7 [26]6  30

   [x]k = the k-th cell we visit
steprcvaluedecision
1041515 < 18 → down
2141919 > 18 → left
3131212 < 18 → down
4231616 < 18 → down
5331717 < 18 → down
6432626 > 18 → left
7422323 > 18 → left
8412121 > 18 → left
94018equal → True

The path goes down and left like the steps of a staircase, which is where the name comes from. Note this isn't "linear search": we never look at most of the cells.

9Complexity & remember

Time O(m + n)

The teacher works it out from three walks:

Why 9 and not 5 + 5 = 10? The corner cell where the path turns, (row 4, column 0) here, gets counted twice if you add "all rows" and "all columns". Each step drops one row or one column, and r can grow at most m − 1 times and c can shrink at most n − 1 times, so we visit at most m + n − 1 cells. In big-O that's O(m + n). Not logarithmic, because we never throw away half. We throw away one row or one column at a time.

Space O(1): just r and c.

Which cells can be the starting point? (interview question)

start cellopen directionswant smallerwant biggerusable?
top-left (1)right, downnowhereright or down?no
top-right (15)left, downleftdownyes
bottom-left (18)up, rightuprightyes
bottom-right (30)up, leftup or left?nowhereno
edge cell (like 19 or 24)3 directionstoo many choicesno
inside cell (like 9)4 directionstoo many choicesno
RememberStart at top-right (or bottom-left). Equal → True · too big → left · too small → down. Loop while inside the grid. O(m + n) time, O(1) space. It's called the staircase approach.

Part D · Staircase from the bottom-left corner

The teacher leaves this as homework: start from the other valid corner. Steps 1–2 are the same as Part C. Here is what changes.

3Intuition

At the bottom-left (18): up is smaller (column grows downward) and right is bigger (row grows to the right). So: too big → go up; too small → go right.

4Conditions

5Approach steps

  1. Start at r = m - 1, c = 0.
  2. Equal → True. Bigger → up. Smaller → right.
  3. Out of the grid → False.

6Code (Python)

staircase from bottom-left
class Solution:
    def searchMatrix(self, matrix, target):
        m, n = len(matrix), len(matrix[0])
        r, c = m - 1, 0                  # bottom-left corner
        while r >= 0 and c < n:
            if matrix[r][c] == target:
                return True
            elif matrix[r][c] > target:
                r -= 1                   # too big: go up
            else:
                c += 1                   # too small: go right
        return False

7What changes, line by line

linePart C (top-right)Part D (bottom-left)
startr, c = 0, n - 1r, c = m - 1, 0
whiler < m and c >= 0r >= 0 and c < n
too bigc -= 1 (left)r -= 1 (up)
too smallr += 1 (down)c += 1 (right)

8Dry run (target 5)

          c0    c1    c2    c3    c4
   r0      1     4     7    11    15
   r1      2    [5]5   8    12    19
   r2    [3]3  [6]4    9    16    22
   r3    [10]2  13    14    17    24
   r4    [18]1  21    23    26    30

   [x]k = the k-th cell we visit
steprcvaluedecisionwhat we throw away
1401818 > 5 → uprow 4
2301010 > 5 → uprow 3
32033 < 5 → rightcolumn 0
42166 > 5 → uprow 2
5115equal → True-

9Complexity

Same: O(m + n) time, O(1) space.


Part E · Revision page

Problem 12 (LeetCode 74)Problem 13 (LeetCode 240)
rows sortedyesyes
columns sortedyes (follows)yes
row starts after previous row endsyesno
opened up into one linesortednot sorted
methodbinary search rows, then the rowstaircase from a corner
timeO(log(m·n))O(m + n)
approachtimespace
brute force, every cellO(m·n)O(1)
staircase (top-right or bottom-left)O(m + n), at most m + n − 1 cellsO(1)
If you remember only 5 lines 1. Rows and columns sorted but rows overlap → binary search doesn't apply.
2. Start at a corner where smaller and bigger each have one direction: top-right or bottom-left.
3. Top-right: equal → True, too big → left, too small → down.
4. Every step throws away a full row or column → at most m + n − 1 steps.
5. Use if/elif/else inside while r < m and c >= 0.
Mistakes to avoid ✗ using Problem 12's row binary search here (misses target 4)
✗ starting at top-left or bottom-right (two choices, can't decide)
✗ three separate ifs (in Python, c = -1 silently reads the last column)
✗ wrong loop bounds: top-right needs r < m and c >= 0
✗ calling it "linear" in the interview: it's the staircase, O(m + n)
test it yourself (paste under any solution above)
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]]
s = Solution()
print(s.searchMatrix(matrix, 5))    # True
print(s.searchMatrix(matrix, 20))   # False
print(s.searchMatrix(matrix, 18))   # True  (9 steps from top-right)
print(s.searchMatrix(matrix, 0))    # False (falls off the left)
print(s.searchMatrix([[-5]], -5))   # True

Based on this video: Search a 2D Matrix II | Staircase approach