Sorted matrix search finds a target in a matrix where each row and each column is sorted. Unlike saddleback search, this approach uses divide and conquer to recursively eliminate regions.
Problem
Given a matrix of size such that:
find a position such that:
If no such position exists, return .
Key Idea
Select a middle column and perform binary search on that column to locate a candidate row.
This splits the matrix into regions where the target may still exist. Discard regions that cannot contain the target.
The process repeats recursively on smaller submatrices.
Algorithm
sorted_matrix_search(A, x, top, bottom, left, right):
if top > bottom or left > right:
return (-1, -1)
mid_col = (left + right) // 2
# binary search in mid column
l = top
r = bottom
while l <= r:
mid_row = (l + r) // 2
if A[mid_row][mid_col] == x:
return (mid_row, mid_col)
elif A[mid_row][mid_col] < x:
l = mid_row + 1
else:
r = mid_row - 1
# search in two submatrices
# top-right
res = sorted_matrix_search(A, x, top, r, mid_col + 1, right)
if res != (-1, -1):
return res
# bottom-left
return sorted_matrix_search(A, x, l, bottom, left, mid_col - 1)Wrapper:
search(A, x):
return sorted_matrix_search(A, x, 0, n-1, 0, m-1)Example
Let
and
The algorithm narrows the search to the appropriate submatrix and eventually finds:
Correctness
Binary search in the middle column identifies a boundary row:
- All elements above are
- All elements below are
If the target is not found, only two subregions can still contain it:
- upper-right region
- lower-left region
Other regions are eliminated because their values are strictly too small or too large.
Recursive calls preserve correctness by only exploring feasible regions.
Complexity
Let be rows and be columns.
| cost | value |
|---|---|
| time | or depending on orientation |
| space | recursion |
This is typically slower than saddleback search for a single query.
Comparison
| method | requirement | time |
|---|---|---|
| saddleback search | row and column sorted | |
| divide and conquer | row and column sorted |
Saddleback search is simpler and faster in most cases.
When to Use
- When recursive decomposition fits the problem structure
- When extending to higher dimensions
- When combining with other divide-and-conquer strategies
Notes
- More complex than necessary for simple lookup
- Demonstrates matrix partitioning technique
- Can be adapted for parallel execution
Implementation
def sorted_matrix_search(a, x):
if not a or not a[0]:
return -1, -1
n, m = len(a), len(a[0])
def helper(top, bottom, left, right):
if top > bottom or left > right:
return -1, -1
mid_col = (left + right) // 2
l, r = top, bottom
while l <= r:
mid_row = (l + r) // 2
if a[mid_row][mid_col] == x:
return mid_row, mid_col
elif a[mid_row][mid_col] < x:
l = mid_row + 1
else:
r = mid_row - 1
res = helper(top, r, mid_col + 1, right)
if res != (-1, -1):
return res
return helper(l, bottom, left, mid_col - 1)
return helper(0, n - 1, 0, m - 1)func SortedMatrixSearch(a [][]int, x int) (int, int) {
if len(a) == 0 || len(a[0]) == 0 {
return -1, -1
}
var helper func(int, int, int, int) (int, int)
helper = func(top, bottom, left, right int) (int, int) {
if top > bottom || left > right {
return -1, -1
}
midCol := (left + right) / 2
l, r := top, bottom
for l <= r {
midRow := (l + r) / 2
if a[midRow][midCol] == x {
return midRow, midCol
} else if a[midRow][midCol] < x {
l = midRow + 1
} else {
r = midRow - 1
}
}
if i, j := helper(top, r, midCol+1, right); i != -1 {
return i, j
}
return helper(l, bottom, left, midCol-1)
}
return helper(0, len(a)-1, 0, len(a[0])-1)
}