Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

Algorithms are a cornerstone of the C++ Standard Library. Unlike container member functions, standard algorithms in <algorithm> and <numeric> operate on iterator ranges rather than container objects directly. This design means the same std::sort can sort a vector, an array, a deque, or any other contiguous or random-access sequence — even a raw pointer range. Algorithms never access the container; they see only the elements through iterators, which makes them generic, composable, and independently testable.
The algorithm range convention is [first, last) — a half-open interval that includes the element at first but excludes the element at last. This is the iterator returned by container.end(). Passing an invalid or reversed range is undefined behavior.

Sorting Algorithms

std::sort — O(n log n) Unstable Sort

The standard general-purpose sort. Uses an introsort (hybrid of quicksort, heapsort, and insertion sort) in MSVC’s implementation.
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>

int main()
{
    // Sort integers ascending (default)
    std::vector<int> nums = {5, 2, 8, 1, 9, 3};
    std::sort(nums.begin(), nums.end());
    for (int x : nums) std::cout << x << ' '; // 1 2 3 5 8 9
    std::cout << "\n";

    // Sort descending with a comparator lambda
    std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
    for (int x : nums) std::cout << x << ' '; // 9 8 5 3 2 1
    std::cout << "\n";

    // Sort strings by length, then lexicographically
    std::vector<std::string> words = {"banana", "fig", "apple", "kiwi", "date"};
    std::sort(words.begin(), words.end(), [](const std::string& a, const std::string& b) {
        return a.size() != b.size() ? a.size() < b.size() : a < b;
    });
    for (const auto& w : words) std::cout << w << ' '; // fig date kiwi apple banana
    std::cout << "\n";
}

std::stable_sort — Preserves Equal-Element Order

stable_sort is identical to sort except it preserves the relative order of equivalent elements. This matters when elements carry extra data beyond their sort key.
#include <algorithm>
#include <vector>
#include <iostream>

struct Task {
    int priority;
    std::string name;
};

int main()
{
    std::vector<Task> tasks = {
        {2, "compile"}, {1, "test"}, {2, "link"}, {1, "deploy"}
    };

    // stable_sort guarantees "compile" precedes "link" (both priority 2)
    std::stable_sort(tasks.begin(), tasks.end(),
        [](const Task& a, const Task& b) { return a.priority < b.priority; });

    for (const auto& t : tasks)
        std::cout << t.priority << " " << t.name << "\n";
    // 1 test
    // 1 deploy
    // 2 compile
    // 2 link
}

std::partial_sort and std::nth_element

When you only need the top-k elements, these algorithms are far more efficient than a full sort:
#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> v = {7, 1, 9, 3, 5, 2, 8, 4, 6};

    // partial_sort: first 3 elements are the 3 smallest, fully sorted
    std::partial_sort(v.begin(), v.begin() + 3, v.end());
    std::cout << "3 smallest: " << v[0] << ' ' << v[1] << ' ' << v[2] << "\n"; // 1 2 3

    // nth_element: v[4] is the element that would be at index 4 if sorted
    // Elements before it are <= v[4]; elements after are >= v[4]
    std::nth_element(v.begin(), v.begin() + 4, v.end());
    std::cout << "median-ish: " << v[4] << "\n";
}

Searching Algorithms

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

int main()
{
    std::vector<int> v = {10, 20, 30, 40, 50};

    // find: returns iterator to first match, or end() if not found
    auto it = std::find(v.begin(), v.end(), 30);
    if (it != v.end())
        std::cout << "Found 30 at index " << std::distance(v.begin(), it) << "\n"; // 2

    // find_if: search with a predicate
    auto it2 = std::find_if(v.begin(), v.end(), [](int x) { return x > 25; });
    if (it2 != v.end())
        std::cout << "First > 25: " << *it2 << "\n"; // 30

    // find_if_not: first element that does NOT satisfy the predicate
    auto it3 = std::find_if_not(v.begin(), v.end(), [](int x) { return x < 35; });
    std::cout << "First >= 35: " << *it3 << "\n"; // 40
}

std::binary_search, std::lower_bound, std::upper_bound

Binary search algorithms require the range to be sorted with respect to the comparison. They run in O(log n).
#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> sorted = {1, 3, 5, 7, 9, 11, 13};

    // binary_search: returns true/false
    std::cout << std::boolalpha;
    std::cout << "has 7?  " << std::binary_search(sorted.begin(), sorted.end(), 7)  << "\n"; // true
    std::cout << "has 6?  " << std::binary_search(sorted.begin(), sorted.end(), 6)  << "\n"; // false

    // lower_bound: iterator to first element >= value
    auto lb = std::lower_bound(sorted.begin(), sorted.end(), 7);
    std::cout << "lower_bound(7): " << *lb << "\n"; // 7

    // upper_bound: iterator to first element > value
    auto ub = std::upper_bound(sorted.begin(), sorted.end(), 7);
    std::cout << "upper_bound(7): " << *ub << "\n"; // 9

    // equal_range returns [lower_bound, upper_bound) as a pair
    auto [first, last] = std::equal_range(sorted.begin(), sorted.end(), 7);
    std::cout << "range size: " << std::distance(first, last) << "\n"; // 1
}

Quantifier Algorithms — all_of, any_of, none_of

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

int main()
{
    std::vector<int> v = {2, 4, 6, 8, 10};

    std::cout << std::boolalpha;
    std::cout << "all even:  " << std::all_of (v.begin(), v.end(), [](int x){ return x % 2 == 0; }) << "\n"; // true
    std::cout << "any > 5:   " << std::any_of (v.begin(), v.end(), [](int x){ return x > 5; })     << "\n"; // true
    std::cout << "none neg:  " << std::none_of(v.begin(), v.end(), [](int x){ return x < 0; })     << "\n"; // true
}

Transforming Algorithms

std::for_each — Apply a Function to Every Element

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

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};

    std::for_each(v.begin(), v.end(), [](int& x) { x *= x; });  // square in place

    for (int x : v) std::cout << x << ' '; // 1 4 9 16 25
    std::cout << "\n";
}

std::transform — Map Elements to a New Range

#include <algorithm>
#include <vector>
#include <string>
#include <cctype>
#include <iostream>

int main()
{
    // Unary transform: double each element into a new vector
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dst(src.size());
    std::transform(src.begin(), src.end(), dst.begin(), [](int x) { return x * 2; });
    for (int x : dst) std::cout << x << ' '; // 2 4 6 8 10
    std::cout << "\n";

    // Binary transform: add two vectors element-wise
    std::vector<int> a = {1, 2, 3};
    std::vector<int> b = {10, 20, 30};
    std::vector<int> c(3);
    std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::plus<int>{});
    for (int x : c) std::cout << x << ' '; // 11 22 33
    std::cout << "\n";

    // Convert string to uppercase
    std::string s = "hello, world!";
    std::transform(s.begin(), s.end(), s.begin(), [](unsigned char ch) {
        return static_cast<char>(std::toupper(ch));
    });
    std::cout << s << "\n"; // HELLO, WORLD!
}

std::copy, std::copy_if, std::remove_copy_if

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

int main()
{
    std::vector<int> src = {1, 2, 3, 4, 5, 6, 7, 8};

    // copy_if: filter even numbers into a new vector
    std::vector<int> evens;
    std::copy_if(src.begin(), src.end(), std::back_inserter(evens),
                 [](int x) { return x % 2 == 0; });

    for (int x : evens) std::cout << x << ' '; // 2 4 6 8
    std::cout << "\n";
}

std::remove / std::remove_if — Erase-Remove Idiom

std::remove does not actually erase elements; it shifts unwanted elements to the back and returns an iterator to the new logical end. Pair it with container.erase() to truly remove elements.
#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};

    // Erase-remove idiom
    v.erase(std::remove(v.begin(), v.end(), 2), v.end());
    for (int x : v) std::cout << x << ' '; // 1 3 4 5
    std::cout << "\n";

    // C++20: std::erase (free function) does both steps
    // std::erase(v, 2);  // equivalent one-liner in C++20
}

Numeric Algorithms (<numeric>)

std::accumulate — Fold / Reduce

#include <numeric>
#include <vector>
#include <string>
#include <iostream>

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};

    // Sum (default operation is addition)
    int sum = std::accumulate(v.begin(), v.end(), 0);
    std::cout << "sum: " << sum << "\n"; // 15

    // Product (custom binary operation)
    int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>{});
    std::cout << "product: " << product << "\n"; // 120

    // Build a string by folding
    std::vector<std::string> words = {"hello", " ", "world"};
    std::string sentence = std::accumulate(words.begin(), words.end(), std::string{});
    std::cout << sentence << "\n"; // hello world
}

std::reduce — Parallel-Friendly Accumulate (C++17)

std::reduce is like accumulate but the binary operation may be applied in any order, enabling parallel execution policies.
#include <numeric>
#include <vector>
#include <execution>
#include <iostream>

int main()
{
    std::vector<double> data(1'000'000, 1.0);

    // Sequential reduce
    double s1 = std::reduce(data.begin(), data.end());
    std::cout << "sequential: " << s1 << "\n";

    // Parallel reduce (requires /std:c++17 and <execution>)
    double s2 = std::reduce(std::execution::par, data.begin(), data.end());
    std::cout << "parallel:   " << s2 << "\n";
}

Other <numeric> Algorithms

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

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};

    // Prefix sums (exclusive scan / inclusive scan in C++17)
    std::vector<int> prefix(v.size());
    std::inclusive_scan(v.begin(), v.end(), prefix.begin());
    for (int x : prefix) std::cout << x << ' '; // 1 3 6 10 15
    std::cout << "\n";

    // Inner product (dot product)
    std::vector<int> a = {1, 2, 3};
    std::vector<int> b = {4, 5, 6};
    int dot = std::inner_product(a.begin(), a.end(), b.begin(), 0);
    std::cout << "dot: " << dot << "\n"; // 32

    // iota: fill with sequential values
    std::vector<int> seq(5);
    std::iota(seq.begin(), seq.end(), 10); // 10 11 12 13 14
    for (int x : seq) std::cout << x << ' ';
    std::cout << "\n";
}

C++20 Ranges

The Ranges library in C++20 introduces range adaptors and views that compose lazily without allocating intermediate containers. Ranges algorithms live in the std::ranges namespace.
#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> v = {5, 3, 1, 4, 2};

    // std::ranges::sort — takes a range directly, no begin/end needed
    std::ranges::sort(v);
    for (int x : v) std::cout << x << ' '; // 1 2 3 4 5
    std::cout << "\n";

    // Lazy pipeline: filter evens, double them, take first 3
    auto result = v
        | std::views::filter([](int x) { return x % 2 == 0; })
        | std::views::transform([](int x) { return x * 2; })
        | std::views::take(2);

    for (int x : result) std::cout << x << ' '; // 4 8
    std::cout << "\n";

    // ranges::find with a projection (sort/search by field without a custom comparator)
    struct Point { int x, y; };
    std::vector<Point> pts = {{1, 5}, {2, 3}, {3, 8}};
    auto it = std::ranges::find(pts, 3, &Point::y);
    if (it != pts.end())
        std::cout << "Found point with y=3: (" << it->x << ',' << it->y << ")\n";
}
Compile with /std:c++20 (or /std:c++latest) to enable <ranges>. Views are lazy — they compute values only when iterated. This makes pipelines like filter | transform | take highly efficient even on large datasets.

Algorithm Categories at a Glance

sort, stable_sort, partial_sort, nth_element, sort_heap, make_heap, push_heap, pop_heap
find, find_if, find_if_not, binary_search, lower_bound, upper_bound, equal_range, search, adjacent_find
all_of, any_of, none_of, count, count_if
transform, copy, copy_if, copy_n, move, fill, fill_n, generate, replace, replace_if, reverse, rotate, shuffle
remove, remove_if, unique — always pair with container.erase() (erase-remove idiom)
accumulate, reduce, transform_reduce, inner_product, partial_sum, inclusive_scan, exclusive_scan, iota, gcd, lcm
set_union, set_intersection, set_difference, set_symmetric_difference, includes, merge, inplace_merge

See Also

Build docs developers (and LLMs) love