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.
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:
- Go through each row one by one
- For each row, use binary search with the template to find the first index where
row[j] >= target - If we find a valid index and
row[first_true_index] == target, returntrue - 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
391class 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}
401class 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};
331/**
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}
37Solution 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:
-
Iterate through each row: We loop through every row in the matrix.
-
Apply binary search template on each row: For each row, use the template to find the first index where
row[mid] >= target. -
Check if target is found: After binary search:
- If
first_true_index != -1androw[first_true_index] == target, we've found the target - Return
Trueimmediately
- If
-
Return result: If we've searched all rows without finding the target, return
False.
Key Implementation Details:
- Uses
while left <= rightwithright = mid - 1when feasible - Tracks answer with
first_true_index = -1initialization - 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 EvaluatorExample 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 = -1mid = 1:row[1] = 3 < 7→left = 2mid = 2:row[2] = 5 < 7→left = 3mid = 3:row[3] = 7 >= 7→first_true_index = 3,right = 2left > right, exit loop
- Check:
first_true_index = 3, androw[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
leftkeeps moving right left > right, exit loop withfirst_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
- All elements < 9,
- Target not in this row
Row 2: [10, 11, 12, 13]
- Binary search with template:
mid = 1:row[1] = 11 >= 9→first_true_index = 1,right = 0mid = 0:row[0] = 10 >= 9→first_true_index = 0,right = -1left > right, exit loop
first_true_index = 0, butrow[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
mrows of the matrix - For each row,
bisect_leftperforms a binary search which takesO(log n)time (since each row hasnelements) - 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
jstores the index returned bybisect_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.
Which of the following is a min heap? 
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
https assets algo monster cover_photos dnd svg Divide and Conquer The main idea of divide and conquer is to split the main problem into two smaller components assuming that each one of the components had already been solved recursively and then try to solve the bigger problem using the solutions
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!