sort, binary_search, lower_bound & upper_bound

RoadmapsC++

Scenario

You need to find the closest value to X in a massive sorted array. You write a custom binary search with `while (left <= right)`. After 30 minutes of debugging infinite loops and off-by-one errors, your interviewer tells you C++ has a 1-line function for this.

Which STL algorithm instantly solves the "closest value" binary search problem?

Mental model

`std::sort` is a highly optimized robot that organizes your bookshelf. `std::binary_search` just tells you "Yes" or "No" if a book exists. `std::lower_bound` is a librarian who walks you to the exact spot where a book is, or where it *should* be inserted if it's missing.

The `<algorithm>` header provides robust, heavily optimized implementations of common algorithms. `std::sort` is an O(N log N) hybrid sorting algorithm. For sorted ranges, the binary search family (`binary_search`, `lower_bound`, `upper_bound`) provides O(log N) lookups without writing manual loops.

Explanation

**1. std::sort**

`std::sort(vec.begin(), vec.end())` sorts elements in ascending order. It uses *IntroSort*: it starts with QuickSort, but if the recursion depth gets too deep, it switches to HeapSort to guarantee O(N log N) worst-case. For tiny sub-arrays, it switches to InsertionSort. You should *never* write your own sorting algorithm in production.

**2. The Binary Search Family**

These algorithms **require** the range to already be sorted. If it's not sorted, the result is undefined garbage.

- `std::binary_search`: Returns a simple `bool` (true if found, false if not).

- `std::lower_bound`: Returns an iterator to the **first element that is >=** the target.

- `std::upper_bound`: Returns an iterator to the **first element that is strictly >** the target.

If no such element exists in the container (i.e. the target is greater than all elements), both bounds safely return the `.end()` iterator.

**Real-world Engineering:**

`lower_bound` is incredibly powerful. It is used in databases to find insertion points for indexing, in game physics to find the nearest collision frame in a timeline, and in interviews to solve almost any "find the closest number" problem without writing manual `while` loops.

Code examples

Using lower_bound and upper_bound

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {10, 20, 20, 20, 30, 40};
    
    // Find the first element >= 20
    auto lb = std::lower_bound(vec.begin(), vec.end(), 20);
    std::cout << "Lower bound index: " << (lb - vec.begin()) << '\n'; // Prints 1
    
    // Find the first element strictly > 20
    auto ub = std::upper_bound(vec.begin(), vec.end(), 20);
    std::cout << "Upper bound index: " << (ub - vec.begin()) << '\n'; // Prints 4
    
    // The number of 20s in the vector is simply the distance between them:
    std::cout << "Count of 20s: " << (ub - lb) << '\n'; // Prints 3
    
    return 0;
}

Subtracting `vec.begin()` from the resulting iterator gives you the exact integer index. The range `[lb, ub)` perfectly encapsulates all instances of the target.

Key points

Common mistakes

Recall questions

Questions & answers

A candidate is asked to find the first number greater than or equal to `X` in a sorted vector. They write `std::lower_bound(vec.begin(), vec.end(), X)`. What happens if all elements in the vector are strictly less than `X`?

The function will return the `vec.end()` iterator. This is why you must always check `if (it != vec.end())` before dereferencing the result of `lower_bound`.

Approach: Identify the boundary conditions and return values of standard search algorithms.

How can you use `lower_bound` and `upper_bound` to efficiently count the occurrences of a number in a sorted array?

You find the `lower_bound` (start of the duplicates) and the `upper_bound` (end of the duplicates). Subtracting the two iterators (`std::distance(lb, ub)`) gives the exact count in O(log N) time.

Approach: Combine boundary algorithms to extract ranges.

Continue learning

Previous: vector — internals, push_back amortization

Return to C++ Roadmap