Upper Bound
Find the first index where elements become strictly greater than a given value.
Upper Bound
Upper bound finds the first index at which elements become strictly greater than a value $x$. It returns the smallest index $i$ such that
$$ A[i] > x $$
If no such index exists, it returns $n$.
This operation complements lower bound and allows precise range queries over equal values.
Problem
Given a sorted array $A$ of length $n$ and a value $x$, find the smallest index $i$ such that
$$ A[i] > x $$
Return $n$ if all elements are $\le x$.
Key Idea
The predicate
$$ A[i] > x $$
is monotone:
- false for smaller indices
- true for larger indices
Binary search isolates the first true position.
Algorithm
Use a half-open interval $[l, r)$.
upper_bound(A, x):
l = 0
r = length(A)
while l < r:
m = l + (r - l) // 2
if A[m] <= x:
l = m + 1
else:
r = m
return l
Example
Let
$$ A = [1, 3, 3, 5, 7] $$
and
$$ x = 3 $$
Steps:
| step | l | r | m | A[m] | action |
|---|---|---|---|---|---|
| 1 | 0 | 5 | 2 | 3 | move right |
| 2 | 3 | 5 | 4 | 7 | move left |
| 3 | 3 | 4 | 3 | 5 | move left |
Return index $3$.
Interpretation
- All indices $< i$ satisfy $A[j] \le x$
- All indices $\ge i$ satisfy $A[j] > x$
This splits the array into two regions.
Relation to Lower Bound
| operation | condition | meaning |
|---|---|---|
| lower bound | $A[i] \ge x$ | first occurrence of $x$ or insertion point |
| upper bound | $A[i] > x$ | position after last occurrence of $x$ |
Range of equal elements:
$$ [\text{lower_bound}(x), \text{upper_bound}(x)) $$
Correctness
Invariant:
- The answer remains in $[l, r)$
Each step eliminates half the interval while preserving:
- left side contains only false values
- right side contains possible true values
Termination at $l = r$ yields the first true index.
Complexity
| case | time |
|---|---|
| all cases | $O(\log n)$ |
Space:
$$ O(1) $$
Use Cases
- Count elements $\le x$
- Find right boundary of duplicates
- Range counting and interval queries
- Partitioning sorted data
Implementation
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
func UpperBound(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
}