upper_bound is a Standard Template Library (STL) function used to find the first element that is strictly greater than a given value in a sorted range. It is commonly used for efficient searching and range queries in sorted containers.
Example: Basic Usage with Vector
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v = {10, 20, 30, 40, 50};
// Finding upper bound for value 30 in vector v
cout << *upper_bound(v.begin(), v.end(), 30);
return 0;
}
Output
40
Explanation:
- The vector is already sorted
- upper_bound(30) skips 10, 20, 30
- Returns iterator pointing to 40
- Dereferencing the iterator prints 40
Syntax
iterator upper_bound(iterator first, iterator last, const T& value);
Parameters
- first: Iterator to the first element of the range
- last: Iterator to one past the last element of the range
- value: The value to compare against
- comp (optional): Custom comparison function
Return Value
- Returns an iterator to the first element greater than value
- Returns last if no such element exists
Note: The behavior of std::upper_bound() is undefined if the range is not sorted
The below examples demonstrate some of its common uses.
Example 1: Find Upper Bound in an Array
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int arr[5] = {10, 20, 30, 40, 50};
int n = sizeof(arr) / sizeof(arr[0]);
// Finding upper bound for value 30 in array arr
cout << *upper_bound(arr, arr + n, 30);
return 0;
}
Output
40
Explanation: This code uses upper_bound() to find and print the first element in the sorted array that is strictly greater than 30, which is 40.
Example 2: Use upper_bound() with Custom Comparator
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
bool comp(const string &a, const string &b)
{
return lexicographical_compare(a.begin(), a.end(), b.begin(), b.end(),
[](char c1, char c2) { return tolower(c1) < tolower(c2); });
}
int main()
{
vector<string> v = {"Apple", "banana", "Cherry", "date", "Elderberry"};
// Finding upper bound of "Avocado"
auto ub = upper_bound(v.begin(), v.end(), "Avocado", comp);
if (ub != v.end())
cout << *ub;
else
cout << "Upper bound not found!";
return 0;
}
Output
banana
Explanation: We need to use the custom comparator function to perform the case insensitive search as the default comparator treats the uppercase and lowercase differently.
Example 3: Check for an Element in a Sorted Vector
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v = {10, 20, 30, 40, 50};
int val = 40;
// Finding the upper bound
auto it = upper_bound(v.begin(), v.end(), val);
// Chekcing if val exists or not
if (it != v.begin() && *(--it) == val)
cout << val << " is found.";
else
cout << val << " is NOT found.";
return 0;
}
Output
40 is found.
Explanation: upper_bound() returns the iterator just after the matching value; to check if the value exists, decrement the iterator and compare.
Example 4: Count Elements Smaller and Larger than a Value
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v = {10, 20, 30, 40, 50};
int val = 30;
// Finding the upper bound of val in v
auto ub = upper_bound(v.begin(), v.end(), val);
// Finding the number of smaller elements
cout << "No. of Smaller Elements: " << ub - v.begin() << endl;
// Finding the number of larger elements
cout << "No. of Larger Elements: " << v.end() - ub;
return 0;
}
Output
No. of Smaller Elements: 3 No. of Larger Elements: 2
Explanation: Counts elements based on the position returned by upper_bound().
Finding Upper Bound in a Set
It is recommended to use set::upper_bound() for set as sets do not have random access to its elements. So, the upper_bound() function have to increment it sequentially to find the middle element (part of binary search algorithm) each time leading to increased time complexity.
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
int main(){
set<int> s = {1, 2, 4, 4, 6, 8};
// Recommended: O(log n)
auto it1 = s.upper_bound(4);
if (it1 != s.end())
cout << "set::upper_bound -> " << *it1 << endl;
else
cout << "set::upper_bound -> Not found" << endl;
// Not recommended: O(n) for set
auto it2 = upper_bound(s.begin(), s.end(), 4);
if (it2 != s.end())
cout << "std::upper_bound -> " << *it2 << endl;
else
cout << "std::upper_bound -> Not found" << endl;
return 0;
}
Output
set::upper_bound -> 6 std::upper_bound -> 6
Explanation:
- set::upper_bound() uses the Red-Black Tree structure of set, giving O(log n) time complexity.
- std::upper_bound() cannot perform binary search efficiently on set because it lacks random-access iterators, resulting in O(n) traversal.
- Both return the same value, but performance differs significantly.