Equal Range Search
Find the range of indices containing all occurrences of a value in a sorted array.
Equal Range Search
Equal range search finds the contiguous range of indices where a value $x$ appears in a sorted array. It returns a pair of indices:
$$ [l, r) $$
such that all elements equal to $x$ lie within this interval.
This operation combines lower bound and upper bound.
Problem
Given a sorted array $A$ of length $n$ and a value $x$, find indices $l$ and $r$ such that:
$$ A[i] = x \quad \text{for all } i \in [l, r) $$
If $x$ does not appear, return an empty range where $l = r$.
Key Idea
Compute:
- $l = \text{lower_bound}(x)$
- $r = \text{upper_bound}(x)$
Then:
$$ [l, r) $$
contains all occurrences of $x$.
Algorithm
equal_range(A, x):
l = lower_bound(A, x)
r = upper_bound(A, x)
return (l, r)
Example
Let
$$ A = [1, 3, 3, 3, 5, 7] $$
and
$$ x = 3 $$
Then:
- lower bound = $1$
- upper bound = $4$
So the result is:
$$ [1, 4) $$
Interpretation
- Elements in $[l, r)$ are equal to $x$
- Elements before $l$ are less than $x$
- Elements from $r$ onward are greater than $x$
This forms a clean partition of the array.
Correctness
Lower bound ensures:
$$ A[l] \ge x $$
Upper bound ensures:
$$ A[r] > x $$
Together:
- all indices in $[l, r)$ satisfy $A[i] = x$
- no index outside the range contains $x$
Complexity
| operation | time |
|---|---|
| lower bound | $O(\log n)$ |
| upper bound | $O(\log n)$ |
| total | $O(\log n)$ |
Space:
$$ O(1) $$
Use Cases
- Count occurrences:
$$ r - l $$
- Range queries in sorted arrays
- Deduplication boundaries
- Frequency counting without scanning
Implementation
def equal_range(a, x):
def lower_bound(a, x):
l, r = 0, len(a)
while l < r:
m = l + (r - l) // 2
if a[m] < x:
l = m + 1
else:
r = m
return l
def upper_bound(a, x):
l, r = 0, len(a)
while l < r:
m = l + (r - l) // 2
if a[m] <= x:
l = m + 1
else:
r = m
return l
l = lower_bound(a, x)
r = upper_bound(a, x)
return (l, r)
func EqualRange(a []int, x int) (int, int) {
lower := func(a []int, x int) int {
l, r := 0, len(a)
for l < r {
m := l + (r-l)/2
if a[m] < x {
l = m + 1
} else {
r = m
}
}
return l
}
upper := func(a []int, x int) int {
l, r := 0, len(a)
for l < r {
m := l + (r-l)/2
if a[m] <= x {
l = m + 1
} else {
r = m
}
}
return l
}
l := lower(a, x)
r := upper(a, x)
return l, r
}