LearnFrenzy
Sign in Take a free test

STL Algorithms and Ranges · Lesson 26 of 33

Binary Search in C++: lower_bound and upper_bound

5 min read Updated Nov 7, 2026 Saurabh Samir

Binary search finds a value in sorted data by halving the search range each step – about 20 steps for a million elements. The STL gives you three tools – binary_search, lower_bound and upper_bound – and the same halving idea, applied to the answer instead of an array, cracks many hard optimisation problems in contests.

In this lesson

  • binary_search, lower_bound, upper_bound, equal_range
  • Counting occurrences and values in a range
  • Writing binary search by hand without bugs
  • Binary search on the answer

The STL functions

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

int main() {
    std::vector<int> v{3, 8, 8, 8, 15, 23, 42};     // must be sorted
    std::cout << std::boolalpha << std::binary_search(v.begin(), v.end(), 15) << ' '
              << std::binary_search(v.begin(), v.end(), 9) << '\n';

    auto lo = std::lower_bound(v.begin(), v.end(), 8);    // first element >= 8
    auto hi = std::upper_bound(v.begin(), v.end(), 8);    // first element > 8
    std::cout << "8 occupies indexes [" << lo - v.begin() << ", " << hi - v.begin() << "), count "
              << hi - lo << '\n';

    auto it = std::lower_bound(v.begin(), v.end(), 20);   // where 20 would be inserted
    std::cout << "first >= 20 is " << *it << " at index " << it - v.begin() << '\n';
    if (std::lower_bound(v.begin(), v.end(), 50) == v.end()) std::cout << "no element >= 50\n";

    int in_range = std::upper_bound(v.begin(), v.end(), 23) - std::lower_bound(v.begin(), v.end(), 8);
    std::cout << in_range << " values in [8, 23]\n";
    return 0;
}

Output

true false
8 occupies indexes [1, 4), count 3
first >= 20 is 23 at index 5
no element >= 50
5 values in [8, 23]

lower_bound(8) and upper_bound(8) on 3 8 8 8 15 23 42

  • 30
  • 81 ← lower_bound
  • 82
  • 83
  • 154 ← upper_bound
  • 235
  • 426
FunctionReturns
binary_search(b, e, x)true/false: is x present?
lower_bound(b, e, x)iterator to the first element ≥ x (or e)
upper_bound(b, e, x)iterator to the first element > x (or e)
equal_range(b, e, x)the pair {lower_bound, upper_bound}

Two classic mistakes

  • Binary-searching unsorted data – the result is meaningless.
  • Dereferencing the result without checking for end().

Writing binary search by hand

#include <iostream>
#include <vector>

// first index whose value is >= target (like lower_bound); returns n if none
int first_at_least(const std::vector<int>& a, int target) {
    int lo = 0, hi = static_cast<int>(a.size());   // search in [lo, hi)
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;              // avoids overflow of lo + hi
        if (a[mid] < target) lo = mid + 1;         // answer is to the right of mid
        else hi = mid;                             // mid could be the answer
    }
    return lo;
}

int main() {
    std::vector<int> a{2, 4, 4, 9, 11, 15};
    for (int t : {4, 10, 1, 20}) std::cout << t << " -> index " << first_at_least(a, t) << '\n';
    return 0;
}

Output

4 -> index 1
10 -> index 4
1 -> index 0
20 -> index 6

Using a half-open range [lo, hi) and "find the first position where the condition becomes true" avoids the off-by-one bugs and infinite loops that plague hand-written binary search.

Binary search on the answer

Many problems ask for the minimum (or maximum) value that satisfies a condition, where the condition is monotonic: if X works, every larger X works too. Then you can binary-search over possible answers and test each with a quick check.

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

// Split books (pages) among k students in order; minimise the maximum pages any student reads.
bool can_split(const std::vector<int>& pages, int k, long long limit) {
    int students = 1;
    long long current = 0;
    for (int p : pages) {
        if (p > limit) return false;
        if (current + p > limit) { students++; current = 0; }
        current += p;
    }
    return students <= k;
}

int main() {
    std::vector<int> pages{12, 34, 67, 90};
    int k = 2;
    long long lo = *std::max_element(pages.begin(), pages.end());
    long long hi = std::accumulate(pages.begin(), pages.end(), 0LL);
    while (lo < hi) {                                  // first limit for which can_split is true
        long long mid = lo + (hi - lo) / 2;
        if (can_split(pages, k, mid)) hi = mid;
        else lo = mid + 1;
    }
    std::cout << "minimum possible maximum: " << lo << " pages\n";
    return 0;
}

Output

minimum possible maximum: 113 pages

Recognising "binary search on the answer"

  1. The question asks for a min or max valueminimum time, maximum distance, smallest capacity…
  2. A yes/no check is easy for a fixed value"can we finish within T?"
  3. The check is monotonicif T works, any larger T works
  4. Binary search the boundaryO(log range) checks

Interview angle

Book allocation, aggressive cows, Koko eating bananas, minimum days to make bouquets, ship packages within D days – all the same pattern. Spotting the monotonic condition is the whole trick.

Practice

Try it yourself

  1. Find the integer square root of 2,000,000,000 (the largest x with x² ≤ n) by binary search.

    Show solution
    #include <iostream>
    
    int main() {
        long long n = 2'000'000'000, lo = 0, hi = n;
        while (lo < hi) {                       // last x with x*x <= n
            long long mid = lo + (hi - lo + 1) / 2;
            if (mid <= n / mid) lo = mid;       // mid*mid <= n, written without overflow
            else hi = mid - 1;
        }
        std::cout << lo << '\n';
        return 0;
    }

    Output

    44721

Quick check

How many times does 8 appear, using the STL?

upper_bound(…, 8) - lower_bound(…, 8).

Why write lo + (hi - lo) / 2?

To avoid integer overflow of lo + hi.

When can you binary-search on the answer?

When the feasibility check is monotonic in the answer.

Summary

  • On sorted data, lower_bound finds the first ≥ x and upper_bound the first > x, in O(log n).
  • Their difference counts occurrences or values in a range.
  • Binary search on the answer solves min/max problems with a monotonic check.

Interview practice: sorting and searching questions.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links