Upper Bound And Lower Bound C

5 min read

Understanding upper bound and lower bound in C++ is a fundamental skill for any programmer working with the Standard Template Library (STL). This leads to these two functions, defined in the <algorithm> header, are the backbone of efficient searching in sorted ranges. Because of that, while std::binary_search simply tells you if an element exists, lower_bound and upper_bound tell you where it is or where it should be. Mastering them unlocks the ability to handle duplicates, insert elements in sorted order, and solve complex algorithmic problems with logarithmic time complexity Easy to understand, harder to ignore..

What Are Lower Bound and Upper Bound?

At their core, both functions perform a binary search on a sorted range (ascending order by default). They return an iterator pointing to a specific position, but the logic defining that position differs slightly The details matter here..

Lower Bound (std::lower_bound)

The lower_bound function returns an iterator pointing to the first element in the range that is not less than (i.e., greater than or equal to) the given value The details matter here..

  • If the value exists: It points to the first occurrence of that value.
  • If the value does not exist: It points to the position where the value could be inserted without breaking the sorting order (the first element greater than the value).

Syntax:

template 
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value);

Upper Bound (std::upper_bound)

The upper_bound function returns an iterator pointing to the first element in the range that is greater than the given value.

  • If the value exists: It points to the element immediately after the last occurrence of that value.
  • If the value does not exist: It points to the exact same position as lower_bound (the first element greater than the value).

Syntax:

template 
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value);

The Critical Difference: A Visual Analogy

Imagine a sorted array of integers: [10, 20, 20, 20, 30, 40]. You are searching for the value 20.

  • lower_bound(20) acts like a librarian finding the start of the "20" section. It points to the first 20 (index 1).
  • upper_bound(20) acts like a librarian finding the end of the "20" section. It points to the 30 (index 4), which is the first element strictly greater than 20.

The range [lower_bound, upper_bound) represents all elements equal to the searched value. This half-open interval concept is crucial for STL algorithms like std::equal_range Worth knowing..

Time Complexity and Requirements

Both functions operate in O(log N) time complexity for random-access iterators (like std::vector, std::array, std::deque, or raw pointers). For forward iterators (like std::list or std::forward_list), the complexity is O(N) because advancing the iterator takes linear time, though the number of comparisons remains logarithmic That's the part that actually makes a difference..

Mandatory Requirement: The range [first, last) must be sorted according to the same criteria used for comparison (default is operator<). Using these functions on an unsorted range results in undefined behavior.

Practical Code Examples

Let's look at concrete implementations using std::vector.

Basic Usage with Vectors

#include 
#include 
#include  // Required for lower_bound, upper_bound

int main() {
    // Sorted vector with duplicates
    std::vector data = {10, 20, 20, 20, 30, 40, 50};
    int target = 20;

    // 1. And lower_bound: First element >= 20
    auto low = std::lower_bound(data. So naturally, begin(), data. end(), target);
    
    // 2. upper_bound: First element > 20
    auto up = std::upper_bound(data.begin(), data.

    std::cout << "Data: ";
    for(int x : data) std::cout << x << " ";
    std::cout << "\n";

    // Calculating index from iterator
    if (low !begin()) << " (Value: " << *low << ")\n";
    
    if (up !In real terms, end())
        std::cout << "lower_bound index: " << (low - data. Even so, = data. And = data. end())
        std::cout << "upper_bound index: " << (up - data.

    // Count occurrences of 20
    std::cout << "Count of 20: " << (up - low) << "\n"; // Output: 3

    return 0;
}

Output:

Data: 10 20 20 20 30 40 50 
lower_bound index: 1 (Value: 20)
upper_bound index: 4 (Value: 30)
Count of 20: 3

Handling Values Not Found

What happens if we search for 25 (which is not in the vector)?

int target = 25;
auto low = std::lower_bound(data.begin(), data.end(), target);
auto up = std::upper_bound(data.begin(), data.end(), target);

std::cout << "lower_bound points to: " << *low << " (Index: " << (low - data.begin()) << ")\n";   // 30 (Index 4)
std::cout << "Are they equal? begin()) << ")\n"; // 30 (Index 4)
std::cout << "upper_bound points to: " << *up << " (Index: " << (up - data." << (low == up ? 

Both return an iterator pointing to **30** (the first element greater than 25). Since `low == up`, the count is 0, correctly indicating the element is missing.

## Advanced Usage: Custom Comparators

The default behavior uses `operator<` (ascending order). Still, real-world data often requires custom sorting, such as descending order or sorting structs by a specific field. Both functions accept a **Compare** functor as the 4th argument.

### Example: Descending Order Vector

If your vector is sorted **descending** (`{50, 40, 30, 20, 20, 10}`), the default `lower_bound` will fail. That's why you must provide a comparator that matches the sorting logic (i. Worth adding: e. , `std::greater<>` or a lambda returning `a > b`).

```cpp
#include  // for std::greater

std::vector descData = {50, 40, 30, 20, 20, 10};
int target = 20;

// Comparator for descending order: returns true if a > b
auto comp = std::greater(); 

// Note: The logic of "lower_bound" adapts to the comparator.
Also, // With greater, comp(30, 20) is true. auto low = std::lower_bound(descData.comp(20, 20) is false.
// It finds the first element for which comp(element, value) is FALSE.
// So it points to the first 20.
begin(), descData.
Just Published

Recently Written

You Might Find Useful

Other Perspectives

Thank you for reading about Upper Bound And Lower Bound C. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home