closest value
vector search
algorithm efficiency
programming techniques
data analysis

Elegant way to find closest value in a vector from above

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

If “closest value from above” means the smallest element that is greater than or equal to a target, the elegant solution for a sorted C++ vector is std::lower_bound. It performs a binary search and returns the first element that is not less than the target.

That is both clearer and faster than writing a manual loop over a sorted container. The important precondition is that the vector must already be sorted according to the same ordering you search with.

Use std::lower_bound

Here is the canonical pattern:

cpp
1#include <algorithm>
2#include <iostream>
3#include <vector>
4
5int main() {
6    std::vector<int> values {2, 5, 9, 14, 20};
7    int target = 10;
8
9    auto it = std::lower_bound(values.begin(), values.end(), target);
10
11    if (it != values.end()) {
12        std::cout << *it << '\n';
13    } else {
14        std::cout << "no value from above" << '\n';
15    }
16}

For target = 10, the iterator points to 14, which is the smallest value greater than or equal to the target.

Why It Is The Right Tool

std::lower_bound runs in O(log n) time on sorted random-access ranges such as std::vector. That is much better than a linear scan for large inputs, and it expresses the intent directly.

In plain language, it answers:

  • where would this target be inserted without breaking sorted order?
  • what is the first element that is at least this large?

Those are exactly the semantics of “closest from above.”

Handle The “No Match” Case

If every element in the vector is smaller than the target, lower_bound returns values.end(). That is not an error; it is how the algorithm tells you there is no qualifying element.

cpp
1auto it = std::lower_bound(values.begin(), values.end(), 100);
2
3if (it == values.end()) {
4    std::cout << "all values are below the target" << '\n';
5}

This edge case matters because dereferencing end() is undefined behavior.

Returning An Index Instead Of An Iterator

Sometimes you want the position rather than the value. Subtract the beginning iterator.

cpp
1auto it = std::lower_bound(values.begin(), values.end(), target);
2
3if (it != values.end()) {
4    std::size_t index = static_cast<std::size_t>(it - values.begin());
5    std::cout << "index=" << index << ", value=" << *it << '\n';
6}

That is useful when the vector stores thresholds, cut points, or time boundaries and you need the matching slot.

What If The Vector Is Not Sorted

If the vector is unsorted, std::lower_bound does not give a meaningful answer. You have two options:

  • sort the data first, then use lower_bound
  • perform a linear scan and track the smallest qualifying element

For one-off searches on unsorted data, a linear scan may be fine. For repeated searches, sorting once and using binary search is usually much better.

cpp
1#include <limits>
2
3int best = std::numeric_limits<int>::max();
4bool found = false;
5
6for (int x : values) {
7    if (x >= target && (!found || x < best)) {
8        best = x;
9        found = true;
10    }
11}

That version works without sorting, but it costs O(n) per query.

upper_bound Is Different

A nearby algorithm is std::upper_bound, which returns the first element strictly greater than the target. Use that only if equality should be excluded.

cpp
auto it = std::upper_bound(values.begin(), values.end(), target);

That difference matters when the target itself may already be present in the vector.

Common Pitfalls

  • Using lower_bound on an unsorted vector.
  • Dereferencing the iterator without checking for end().
  • Choosing upper_bound when the requirement is greater than or equal to the target.
  • Writing a manual binary search even though the standard library already expresses the intent clearly.
  • Forgetting that the sort order used during search must match the vector's actual ordering.

Summary

  • For a sorted vector, std::lower_bound is the elegant solution.
  • It returns the first element that is not less than the target.
  • Check for end() to detect the case where no qualifying value exists.
  • Use upper_bound only when you need strictly greater values.
  • If the vector is unsorted, sort it first or fall back to a linear scan.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.