Facebook Pixel

240. Search a 2D Matrix II

Problem Description

You are given a 2D integer matrix with special sorting properties and need to search for a target value in it.

The matrix has these characteristics:

  • Each row is sorted in ascending order from left to right
  • Each column is sorted in ascending order from top to bottom

Your task is to write an efficient algorithm that determines whether a given target value exists in the matrix. The function should return true if the target is found, and false otherwise.

For example, consider this 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]

If target = 5, the function should return true since 5 exists in the matrix. If target = 20, the function should return false since 20 does not exist in the matrix.

The challenge is to leverage the sorted properties of the matrix to create an efficient search algorithm rather than checking every element one by one.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since each row is sorted in ascending order, we can take advantage of this property to search more efficiently than checking every element. When we look at any single row, it's just like searching in a regular sorted array - we can use binary search!

The key insight is that we don't need to worry about the column-wise sorting property for this particular approach. We can treat the problem as searching through multiple sorted arrays (each row being one sorted array).

The feasible function: For each position j in a row, we ask: "Is row[j] >= target?" This creates a boolean pattern of [false, false, ..., true, true, ...] in a sorted row. We want to find the first position where this becomes true, and then check if the value there equals exactly target.

The algorithm becomes straightforward:

  1. Go through each row one by one
  2. For each row, use binary search with the template to find the first index where row[j] >= target
  3. If we find a valid index and row[first_true_index] == target, return true
  4. If we've checked all rows and haven't found the target, return false

This approach is more efficient than a naive search because instead of checking all m × n elements, we're doing m binary searches, each taking O(log n) time, giving us O(m × log n) time complexity.

Solution Implementation

1from typing import List
2
3
4class Solution:
5    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
6        """
7        Search for a target value in a 2D matrix.
8        Each row is searched using binary search template.
9
10        Args:
11            matrix: 2D list of integers
12            target: Integer value to search for
13
14        Returns:
15            True if target is found, False otherwise
16        """
17        n = len(matrix[0])
18
19        # Iterate through each row in the matrix
20        for row in matrix:
21            # Binary search template to find first index where row[mid] >= target
22            left, right = 0, n - 1
23            first_true_index = -1
24
25            while left <= right:
26                mid = (left + right) // 2
27                if row[mid] >= target:
28                    first_true_index = mid
29                    right = mid - 1
30                else:
31                    left = mid + 1
32
33            # Check if the position is valid and contains the target value
34            if first_true_index != -1 and row[first_true_index] == target:
35                return True
36
37        # Target not found in any row
38        return False
39
1class Solution {
2    /**
3     * Searches for a target value in a 2D matrix.
4     * Each row in the matrix is searched using binary search template.
5     *
6     * @param matrix The 2D integer matrix to search in
7     * @param target The target value to find
8     * @return true if the target is found, false otherwise
9     */
10    public boolean searchMatrix(int[][] matrix, int target) {
11        int n = matrix[0].length;
12
13        // Iterate through each row in the matrix
14        for (int[] row : matrix) {
15            // Binary search template to find first index where row[mid] >= target
16            int left = 0;
17            int right = n - 1;
18            int firstTrueIndex = -1;
19
20            while (left <= right) {
21                int mid = left + (right - left) / 2;
22                if (row[mid] >= target) {
23                    firstTrueIndex = mid;
24                    right = mid - 1;
25                } else {
26                    left = mid + 1;
27                }
28            }
29
30            // Check if the position is valid and contains the target value
31            if (firstTrueIndex != -1 && row[firstTrueIndex] == target) {
32                return true;
33            }
34        }
35
36        // Target was not found in any row
37        return false;
38    }
39}
40
1class Solution {
2public:
3    bool searchMatrix(vector<vector<int>>& matrix, int target) {
4        int n = matrix[0].size();
5
6        // Iterate through each row of the matrix
7        for (auto& row : matrix) {
8            // Binary search template to find first index where row[mid] >= target
9            int left = 0;
10            int right = n - 1;
11            int firstTrueIndex = -1;
12
13            while (left <= right) {
14                int mid = left + (right - left) / 2;
15                if (row[mid] >= target) {
16                    firstTrueIndex = mid;
17                    right = mid - 1;
18                } else {
19                    left = mid + 1;
20                }
21            }
22
23            // Check if the found position is valid and contains the target value
24            if (firstTrueIndex != -1 && row[firstTrueIndex] == target) {
25                return true;  // Target found
26            }
27        }
28
29        // Target not found in any row
30        return false;
31    }
32};
33
1/**
2 * Searches for a target value in a 2D matrix
3 * Each row is sorted in ascending order
4 * @param matrix - 2D array of numbers to search in
5 * @param target - The value to search for
6 * @returns true if target is found, false otherwise
7 */
8function searchMatrix(matrix: number[][], target: number): boolean {
9  const n = matrix[0].length;
10
11  // Iterate through each row in the matrix
12  for (const row of matrix) {
13    // Binary search template to find first index where row[mid] >= target
14    let left = 0;
15    let right = n - 1;
16    let firstTrueIndex = -1;
17
18    while (left <= right) {
19      const mid = Math.floor((left + right) / 2);
20      if (row[mid] >= target) {
21        firstTrueIndex = mid;
22        right = mid - 1;
23      } else {
24        left = mid + 1;
25      }
26    }
27
28    // Check if the target exists at the found index
29    if (firstTrueIndex !== -1 && row[firstTrueIndex] === target) {
30      return true;
31    }
32  }
33
34  // Target not found in any row
35  return false;
36}
37

Solution Approach

The solution implements a binary search template approach for each row of the matrix.

Binary Search Template: For each row, we search for the first index where row[mid] >= target:

left, right = 0, n - 1
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if row[mid] >= target:  # feasible condition
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

Algorithm Steps:

  1. Iterate through each row: We loop through every row in the matrix.

  2. Apply binary search template on each row: For each row, use the template to find the first index where row[mid] >= target.

  3. Check if target is found: After binary search:

    • If first_true_index != -1 and row[first_true_index] == target, we've found the target
    • Return True immediately
  4. Return result: If we've searched all rows without finding the target, return False.

Key Implementation Details:

  • Uses while left <= right with right = mid - 1 when feasible
  • Tracks answer with first_true_index = -1 initialization
  • Verifies exact match: row[first_true_index] == target

Time Complexity: O(m × log n) where m is the number of rows and n is the number of columns.

Space Complexity: O(1) as we only use a constant amount of extra space.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the binary search template approach.

Consider this 3×4 matrix and search for target = 7:

[1,  3,  5,  7]
[2,  4,  6,  8]
[10, 11, 12, 13]

Step-by-step execution:

Row 0: [1, 3, 5, 7]

  • Binary search with template for first index where row[mid] >= 7:
    • left = 0, right = 3, first_true_index = -1
    • mid = 1: row[1] = 3 < 7left = 2
    • mid = 2: row[2] = 5 < 7left = 3
    • mid = 3: row[3] = 7 >= 7first_true_index = 3, right = 2
    • left > right, exit loop
  • Check: first_true_index = 3, and row[3] = 7 == 7
  • Target found! Return True

Let's see another example where target = 9 (not in matrix):

Row 0: [1, 3, 5, 7]

  • Binary search with template:
    • left = 0, right = 3, first_true_index = -1
    • All elements < 9, so left keeps moving right
    • left > right, exit loop with first_true_index = -1
  • first_true_index = -1, target not in this row

Row 1: [2, 4, 6, 8]

  • Binary search with template:
    • All elements < 9, first_true_index = -1
  • Target not in this row

Row 2: [10, 11, 12, 13]

  • Binary search with template:
    • mid = 1: row[1] = 11 >= 9first_true_index = 1, right = 0
    • mid = 0: row[0] = 10 >= 9first_true_index = 0, right = -1
    • left > right, exit loop
  • first_true_index = 0, but row[0] = 10 ≠ 9
  • Target not in this row

All rows checked, return False

Time and Space Complexity

The time complexity is O(m × log n), where m is the number of rows and n is the number of columns in the matrix. This is because:

  • The outer loop iterates through all m rows of the matrix
  • For each row, bisect_left performs a binary search which takes O(log n) time (since each row has n elements)
  • Therefore, the total time complexity is O(m) × O(log n) = O(m × log n)

The space complexity is O(1). The algorithm only uses a constant amount of extra space:

  • The variable j stores the index returned by bisect_left
  • No additional data structures are created that scale with the input size
  • The iteration through the matrix doesn't require extra space proportional to the input

Common Pitfalls

Pitfall 1: Using the Wrong Binary Search Template Variant

The Issue: Using while left < right with right = mid instead of the correct template.

Incorrect Implementation:

# WRONG: Using incorrect template
while left < right:
    mid = (left + right) // 2
    if row[mid] >= target:
        right = mid
    else:
        left = mid + 1
# Returns insertion point, may be out of bounds

Correct Implementation:

first_true_index = -1
while left <= right:
    mid = (left + right) // 2
    if row[mid] >= target:
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1
# Check first_true_index != -1 before using

Pitfall 2: Not Verifying Exact Match

The Issue: The template finds the first position where row[mid] >= target, but this doesn't guarantee the value equals target.

Incorrect Implementation:

# WRONG: Assumes first_true_index always has the target
if first_true_index != -1:
    return True

Correct Implementation:

# CORRECT: Verify exact match
if first_true_index != -1 and row[first_true_index] == target:
    return True

Pitfall 3: Not Fully Leveraging 2D Sorted Structure

The binary search per row approach doesn't fully exploit the column sorting. A more optimal O(m + n) staircase approach exists:

# Start from top-right corner
row, col = 0, len(matrix[0]) - 1
while row < len(matrix) and col >= 0:
    if matrix[row][col] == target:
        return True
    elif matrix[row][col] > target:
        col -= 1
    else:
        row += 1
return False

However, the binary search approach is still valid and demonstrates consistent template usage.

Pitfall 4: Not Handling Edge Cases

Always check for empty matrices or rows before accessing matrix[0].length.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More