Saddleback Search
Search a matrix sorted by rows and columns using a monotone path from a corner.
Saddleback Search
Saddleback search finds a target in a matrix where each row and each column is sorted in nondecreasing order. It walks through the matrix along a monotone path, eliminating one row or one column at each step.
Problem
Given a matrix $A$ of size $n \times m$ such that:
$$ A[i][j] \le A[i][j+1] \quad \text{(rows sorted)} $$
$$ A[i][j] \le A[i+1][j] \quad \text{(columns sorted)} $$
find a position $(i, j)$ such that:
$$ A[i][j] = x $$
If no such position exists, return $(-1, -1)$.
Key Idea
Start from the top-right corner:
- At $(0, m-1)$
At each step:
- If current value equals $x$, return it
- If current value is greater than $x$, move left
- If current value is less than $x$, move down
Each move discards one row or one column.
Algorithm
saddleback_search(A, x):
n = number of rows
m = number of columns
i = 0
j = m - 1
while i < n and j >= 0:
if A[i][j] == x:
return (i, j)
else if A[i][j] > x:
j = j - 1
else:
i = i + 1
return (-1, -1)
Example
Let
$$
A =
\begin{bmatrix}
1 & 4 & 7 & 11
2 & 5 & 8 & 12
3 & 6 & 9 & 16
10 & 13 & 14 & 17
\end{bmatrix}
$$
and
$$ x = 9 $$
Path:
| step | position | value | action |
|---|---|---|---|
| 1 | (0, 3) | 11 | move left |
| 2 | (0, 2) | 7 | move down |
| 3 | (1, 2) | 8 | move down |
| 4 | (2, 2) | 9 | found |
Return $(2, 2)$.
Correctness
At each step, the algorithm maintains a candidate region where the target may lie.
From the top-right corner:
- All elements below are larger
- All elements to the left are smaller
If $A[i][j] > x$, then every element below in column $j$ is greater than or equal to $A[i][j]$, so none can equal $x$. The column can be discarded.
If $A[i][j] < x$, then every element to the left in row $i$ is less than or equal to $A[i][j]$, so none can equal $x$. The row can be discarded.
Each step reduces the search space while preserving all possible positions of $x$.
Complexity
| dimension | cost |
|---|---|
| rows | $n$ |
| columns | $m$ |
| total | $O(n + m)$ |
Space:
$$ O(1) $$
Comparison
| method | requirement | time |
|---|---|---|
| full scan | none | $O(nm)$ |
| binary search per row | rows sorted | $O(n \log m)$ |
| saddleback search | rows and columns sorted | $O(n + m)$ |
Variants
| start point | direction |
|---|---|
| top-right | move left or down |
| bottom-left | move up or right |
Both give the same complexity.
When to Use
- Matrix sorted by rows and columns
- Efficient elimination of rows or columns
- Large matrices where $n + m \ll nm$
Notes
- Does not require global ordering across rows
- Works with duplicates
- Deterministic path with no backtracking
Implementation
def saddleback_search(a, x):
if not a or not a[0]:
return -1, -1
n, m = len(a), len(a[0])
i, j = 0, m - 1
while i < n and j >= 0:
if a[i][j] == x:
return i, j
elif a[i][j] > x:
j -= 1
else:
i += 1
return -1, -1
func SaddlebackSearch(a [][]int, x int) (int, int) {
if len(a) == 0 || len(a[0]) == 0 {
return -1, -1
}
n, m := len(a), len(a[0])
i, j := 0, m-1
for i < n && j >= 0 {
if a[i][j] == x {
return i, j
} else if a[i][j] > x {
j--
} else {
i++
}
}
return -1, -1
}