Find Upper Bound in Sorted Array
Problem and examples
Given a sorted array of integers and a target number x, your task is to find the upper bound of x in the array.
- The upper bound of
xis defined as the index of the first element in the array that is strictly greater thanx. - If no such element exists (i.e., all elements are ≤
x), return the length of the array.
| Input Array | Key (x) | Upper Bound Index | Value at Upper Bound | Description |
|---|---|---|---|---|
| [10, 20, 30, 40, 50] | 30 | 3 | 40 | First element greater than 30 is at index 3 |
| [10, 20, 30, 40, 50] | 45 | 4 | 50 | 50 is the first element greater than 45 |
| [10, 20, 30, 40, 50] | 50 | 5 | - | No element greater than 50, so return array length 5 |
| [5, 10, 15] | 1 | 0 | 5 | All elements are greater, first is at index 0 |
| [10] | 5 | 0 | 10 | Single element greater than key |
| [10] | 10 | 1 | - | No element greater than 10 |
| [10] | 15 | 1 | - | All elements less than or equal to 15 |
Explore Upper Bound in Sorted Array
Load an example or enter your own input. Use the player controls to follow each change.
Understanding the Problem
We are given a sorted array and a number x. Our goal is to find the upper bound of x. That means we want the index of the first element in the array that is strictly greater than x.
If no element in the array is greater than x, we should return the size of the array — indicating that all elements are less than or equal to x.
Step-by-Step Approach
We will use a technique called Binary Search, which is highly efficient on sorted arrays. This approach repeatedly splits the search range in half to quickly find the answer.
Step 1: Define Pointers
- Set
low = 0(start of the array) - Set
high = arr.length - 1(end of the array) - Set
ans = arr.length— default to the end of array in case we don’t find a greater element
Step 2: Apply Binary Search Logic
We run a loop while low ≤ high. On each iteration:
- Calculate the mid index:
mid = Math.floor((low + high) / 2) - If
arr[mid] > x:- It is a valid candidate for upper bound
- So update
ans = mid - Then try to find a smaller valid index by moving
high = mid - 1
- Else (
arr[mid] ≤ x):- We ignore this part of the array and move right:
low = mid + 1
- We ignore this part of the array and move right:
Step 3: Return the Result
After the loop ends, ans holds the index of the smallest number greater than x. If all elements are ≤ x, it remains at arr.length.
Example Walkthrough
Input: arr = [2, 4, 6, 10, 12], x = 6
Expected Output: 3 (since arr[3] = 10 is the first number > 6)
- Initial: low = 0, high = 4, ans = 5
- mid = 2 → arr[2] = 6 → not greater → move right (low = 3)
- mid = 3 → arr[3] = 10 → greater → ans = 3 → check left (high = 2)
- Loop ends → return ans = 3
Handling Edge Cases
- Case 1:
xis smaller than all elements
Result: returns 0 (first element is upper bound) - Case 2:
xis greater than all elements
Result: returnsarr.length(no element is greater) - Case 3: Array has duplicates
It still finds the first element strictly greater thanx - Case 4: Empty array
Always return 0, as there are no elements to compare
This solution uses binary search, which is optimal for sorted arrays. Instead of checking each element, we skip half the array on each step — making it fast and efficient with a time complexity of O(log n).
- Given a sorted array
arrof N integers and an integerx. - Initialize two pointers:
low = 0andhigh = arr.length - 1. - Initialize
ans = arr.lengthto track the possible answer. - While
low ≤ high: - → Compute
mid = Math.floor((low + high) / 2). - → If
arr[mid] > x, updateans = midand sethigh = mid - 1. - → Else, set
low = mid + 1. - After the loop,
answill hold the index of the upper bound.
Implementation and efficiency
C implementation
#include <stdio.h>
int upperBound(int arr[], int n, int x) {
int low = 0, high = n - 1, ans = n;
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] > x) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
int main() {
int arr[] = {1, 2, 4, 4, 5, 7};
int n = sizeof(arr) / sizeof(arr[0]);
int x = 4;
printf("Upper Bound Index: %d\n", upperBound(arr, n, x));
return 0;
}C++ implementation
#include <iostream>
#include <vector>
using namespace std;
int upperBound(vector<int>& arr, int x) {
int low = 0, high = arr.size() - 1;
int ans = arr.size();
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] > x) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
int main() {
vector<int> arr = {1, 2, 4, 4, 5, 7};
int x = 4;
cout << "Upper Bound Index: " << upperBound(arr, x) << endl;
return 0;
}Python implementation
def upper_bound(arr, x):
low, high = 0, len(arr) - 1
ans = len(arr)
while low <= high:
mid = (low + high) // 2
if arr[mid] > x:
ans = mid
high = mid - 1
else:
low = mid + 1
return ans
# Sample Input
arr = [1, 2, 4, 4, 5, 7]
x = 4
print("Upper Bound Index:", upper_bound(arr, x))Java implementation
public class UpperBound {
public static int upperBound(int[] arr, int x) {
int low = 0, high = arr.length - 1;
int ans = arr.length;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] > x) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
public static void main(String[] args) {
int[] arr = {1, 2, 4, 4, 5, 7};
int x = 4;
System.out.println("Upper Bound Index: " + upperBound(arr, x));
}
}JavaScript implementation
function upperBound(arr, x) {
let low = 0, high = arr.length - 1;
let ans = arr.length;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (arr[mid] > x) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
// Sample Input
const arr = [1, 2, 4, 4, 5, 7];
const x = 4;
console.log("Upper Bound Index:", upperBound(arr, x));Go implementation
package main
import "fmt"
func upperBound(arr []int, x int) int {
low, high := 0, len(arr)-1
ans := len(arr)
for low <= high {
mid := (low + high) / 2
if arr[mid] > x {
ans = mid
high = mid - 1
} else {
low = mid + 1
}
}
return ans
}
func main() {
arr := []int{1, 2, 4, 4, 5, 7}
x := 4
fmt.Println("Upper Bound Index:", upperBound(arr, x))
}Rust implementation
fn upper_bound(arr: &Vec<i32>, x: i32) -> usize {
let (mut low, mut high) = (0, arr.len() as isize - 1);
let mut ans = arr.len();
while low <= high {
let mid = (low + high) / 2;
if arr[mid as usize] > x {
ans = mid as usize;
high = mid - 1;
} else {
low = mid + 1;
}
}
ans
}
fn main() {
let arr = vec![1, 2, 4, 4, 5, 7];
let x = 4;
println!("Upper Bound Index: {}", upper_bound(&arr, x));
}Kotlin implementation
fun upperBound(arr: List<Int>, x: Int): Int {
var low = 0
var high = arr.size - 1
var ans = arr.size
while (low <= high) {
val mid = (low + high) / 2
if (arr[mid] > x) {
ans = mid
high = mid - 1
} else {
low = mid + 1
}
}
return ans
}
fun main() {
val arr = listOf(1, 2, 4, 4, 5, 7)
val x = 4
println("Upper Bound Index: ${upperBound(arr, x)}")
}Swift implementation
func upperBound(_ arr: [Int], _ x: Int) -> Int {
var low = 0
var high = arr.count - 1
var ans = arr.count
while low <= high {
let mid = (low + high) / 2
if arr[mid] > x {
ans = mid
high = mid - 1
} else {
low = mid + 1
}
}
return ans
}
let arr = [1, 2, 4, 4, 5, 7]
let x = 4
print("Upper Bound Index:", upperBound(arr, x))TypeScript implementation
function upperBound(arr: number[], x: number): number {
let low = 0, high = arr.length - 1;
let ans = arr.length;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
if (arr[mid] > x) {
ans = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return ans;
}
const arr: number[] = [1, 2, 4, 4, 5, 7];
const x: number = 4;
console.log("Upper Bound Index:", upperBound(arr, x));Time complexity
| Case | Time | Explanation |
|---|---|---|
| best case | O(1) | The target is immediately found at the mid index in the first comparison. |
| average case | O(log n) | With each iteration, the search space is halved until the upper bound index is found. |
| worst case | O(log n) | The binary search may continue until the search space is reduced to one element before finding the correct index or determining it does not exist. |
Space complexity
O(1)
The algorithm uses a constant amount of extra space regardless of the input size. All operations are done in-place using variables like low, high, mid, and ans.