STL Algorithms: transform, sort, find, and More

Master C++ STL algorithms—sort, find, transform, count, accumulate, remove, and more. Learn how to use the algorithm library with practical step-by-step examples.

STL Algorithms: transform, sort, find, and More

The C++ STL algorithm library (<algorithm> and <numeric>) provides over 100 generic functions that operate on iterator ranges to sort, search, transform, count, and manipulate data in any compatible container. By separating algorithms from data structures through the iterator interface, STL algorithms let you apply the same sort(), find(), or transform() to a vector, deque, array, or any custom container—writing powerful data-processing code without reinventing common operations.

Introduction: Stop Writing Loops, Start Using Algorithms

Every programmer writes the same loops over and over: loop to find an element, loop to sort data, loop to transform values, loop to count matches. These loops are boilerplate—they obscure your actual intent behind implementation details. What you really mean is “find the first element matching this condition” or “apply this transformation to every element.” The STL algorithm library lets you say exactly that.

STL algorithms are generic functions that operate on ranges defined by pairs of iterators. They’re completely decoupled from container types—std::sort doesn’t know or care whether it’s sorting a vector, a deque, or a plain C-style array. This decoupling is what makes the algorithm library so powerful: learn an algorithm once, use it everywhere.

The library covers virtually every common data-processing operation: sorting (sort, stable_sort, partial_sort), searching (find, find_if, binary_search, lower_bound), transformations (transform, copy, copy_if, replace), aggregation (accumulate, count, min_element, max_element), reordering (reverse, rotate, shuffle), and partitioning (partition, stable_partition). Understanding even a subset of these algorithms dramatically improves the expressiveness, correctness, and efficiency of your code.

This comprehensive guide covers the most important and most-used STL algorithms in depth. For each algorithm you’ll see the signature, what it does, when to use it, and step-by-step examples with detailed explanations. By the end, you’ll reach for these algorithms instinctively instead of writing manual loops.

The Algorithm Header and Iterator Ranges

All STL algorithms work on ranges defined by two iterators: a begin and an end (one past the last element).

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
using namespace std;

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

    // Every algorithm takes at least [begin, end) range
    cout << "=== Basic range concept ===" << endl;
    cout << "Full vector:   ";
    for_each(v.begin(), v.end(), [](int n){ cout << n << " "; });
    cout << endl;

    // Can operate on SUB-RANGES — just change iterators
    cout << "Middle 5 elements: ";
    for_each(v.begin() + 2, v.begin() + 7, [](int n){ cout << n << " "; });
    cout << endl;

    // Works on plain arrays too — algorithms don't know it's a vector
    int arr[] = {10, 20, 30, 40, 50};
    cout << "Plain array:   ";
    for_each(arr, arr + 5, [](int n){ cout << n << " "; });
    cout << endl;

    // All algorithms use the same pattern: (begin, end, [extra args])
    int total = accumulate(v.begin(), v.end(), 0);
    cout << "\nSum of vector: " << total << endl;

    int minVal = *min_element(v.begin(), v.end());
    int maxVal = *max_element(v.begin(), v.end());
    cout << "Min: " << minVal << ", Max: " << maxVal << endl;

    return 0;
}

Step-by-step explanation:

  1. #include <algorithm>: Header for most STL algorithms (sort, find, transform, etc.)
  2. #include <numeric>: Header for numeric algorithms (accumulate, iota, inner_product)
  3. for_each(begin, end, func): Calls func on each element in the range — simplest algorithm
  4. v.begin(), v.end(): Full range — all elements
  5. v.begin() + 2, v.begin() + 7: Sub-range — elements at indices 2 through 6
  6. Sub-range flexibility: Every algorithm accepts any valid iterator pair — partial processing is easy
  7. arr, arr + 5: Plain C arrays expose pointer-based “iterators” — algorithms work unchanged
  8. Container agnosticism: for_each doesn’t know or care whether it’s iterating a vector or array
  9. accumulate(begin, end, init): Sums all elements starting from initial value 0
  10. min_element / max_element: Return iterators to min/max element — dereference with *
  11. Why return iterators?: Lets you know the position, not just the value — useful for further operations
  12. Universal pattern: (begin, end, ...) — once you know this pattern, all algorithms feel familiar
  13. Zero runtime overhead: Algorithms compile to the same code as hand-written loops

Output:

Plaintext
=== Basic range concept ===
Full vector:   5 2 8 1 9 3 7 4 6
Middle 5 elements: 8 1 9 3 7
Plain array:   10 20 30 40 50

Sum of vector: 45
Min: 1, Max: 9

Sorting Algorithms

Sorting is one of the most fundamental operations. The STL provides several variants for different needs.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

struct Person {
    string name;
    int age;
    double score;
};

void printPeople(const vector<Person>& people, const string& label) {
    cout << label << ":" << endl;
    for (const auto& p : people) {
        cout << "  " << p.name << " (age " << p.age
             << ", score " << p.score << ")" << endl;
    }
}

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

    // --- sort: O(n log n), not stable ---
    cout << "=== std::sort ===" << endl;
    vector<int> v1 = nums;
    sort(v1.begin(), v1.end());
    cout << "Ascending:  ";
    for (int n : v1) cout << n << " ";
    cout << endl;

    sort(v1.begin(), v1.end(), greater<int>());
    cout << "Descending: ";
    for (int n : v1) cout << n << " ";
    cout << endl;

    // Custom comparator
    sort(v1.begin(), v1.end(), [](int a, int b) {
        return (a % 2 == 0) < (b % 2 == 0);  // Odd numbers first
    });
    cout << "Odd first:  ";
    for (int n : v1) cout << n << " ";
    cout << endl;

    // --- stable_sort: O(n log n), preserves relative order of equal elements ---
    cout << "\n=== std::stable_sort ===" << endl;
    vector<Person> people = {
        {"Alice", 30, 88.5},
        {"Bob",   25, 92.0},
        {"Carol", 30, 75.0},
        {"Diana", 25, 88.5},
        {"Eve",   28, 92.0}
    };
    printPeople(people, "Original");

    stable_sort(people.begin(), people.end(),
                [](const Person& a, const Person& b) {
                    return a.age < b.age;  // Sort by age
                });
    printPeople(people, "\nAfter stable_sort by age (equal ages keep original order)");

    // --- partial_sort: sorts only first k elements ---
    cout << "\n=== std::partial_sort (top 3) ===" << endl;
    vector<int> v2 = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    partial_sort(v2.begin(), v2.begin() + 3, v2.end());
    cout << "First 3 sorted, rest unsorted: ";
    for (int n : v2) cout << n << " ";
    cout << endl;

    // --- nth_element: puts nth element in its sorted position ---
    cout << "\n=== std::nth_element (median) ===" << endl;
    vector<int> v3 = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    auto midIt = v3.begin() + v3.size() / 2;
    nth_element(v3.begin(), midIt, v3.end());
    cout << "Median element: " << *midIt << endl;
    cout << "Array rearranged: ";
    for (int n : v3) cout << n << " ";
    cout << endl;

    // --- is_sorted check ---
    cout << "\n=== std::is_sorted ===" << endl;
    vector<int> sorted   = {1, 2, 3, 4, 5};
    vector<int> unsorted = {1, 3, 2, 4, 5};
    cout << "sorted is sorted:   " << (is_sorted(sorted.begin(), sorted.end()) ? "Yes" : "No") << endl;
    cout << "unsorted is sorted: " << (is_sorted(unsorted.begin(), unsorted.end()) ? "Yes" : "No") << endl;

    return 0;
}

Step-by-step explanation:

  1. sort(begin, end): Default ascending sort — uses introsort (hybrid quicksort/heapsort) — O(n log n)
  2. sort is NOT stable: Equal elements may reorder relative to each other
  3. greater<int>(): Standard comparator functor for descending order
  4. Custom lambda comparator: Returns true when a should come before b
  5. Odd-first comparator: (a % 2 == 0) < (b % 2 == 0) — false(odd) < true(even) → odds first
  6. stable_sort: Same O(n log n) but guarantees equal elements keep their original relative order
  7. When stable matters: Alice and Carol both age 30 — stable_sort preserves Alice before Carol
  8. Diana and Bob both 25: Bob was first, so stays before Diana after stable sort by age
  9. partial_sort(begin, middle, end): Sorts only first (middle-begin) elements — O(n log k)
  10. Use case: Finding top-K without sorting everything — much faster for small k, large n
  11. nth_element: Rearranges so nth position has element it would have if fully sorted — O(n)
  12. Elements before nth: All smaller than or equal to *nth (not necessarily sorted)
  13. Elements after nth: All greater than or equal to *nth (not necessarily sorted)
  14. Median finding: nth_element at midpoint finds median without full sort — O(n) vs O(n log n)
  15. is_sorted: O(n) check — returns true if range is already in non-decreasing order

Output:

Plaintext
=== std::sort ===
Ascending:  1 2 3 4 5 6 7 8 9
Descending: 9 8 7 6 5 4 3 2 1
Odd first:  9 7 5 3 1 2 4 6 8

=== std::stable_sort ===
Original:
  Alice (age 30, score 88.5)
  Bob (age 25, score 92)
  Carol (age 30, score 75)
  Diana (age 25, score 88.5)
  Eve (age 28, score 92)

After stable_sort by age (equal ages keep original order):
  Bob (age 25, score 92)
  Diana (age 25, score 88.5)
  Eve (age 28, score 92)
  Alice (age 30, score 88.5)
  Carol (age 30, score 75)

=== std::partial_sort (top 3) ===
First 3 sorted, rest unsorted: 1 2 3 8 5 9 7 4 6

=== std::nth_element (median) ===
Median element: 5
Array rearranged: 4 2 3 1 5 9 7 8 6

=== std::is_sorted ===
sorted is sorted:   Yes
unsorted is sorted: No

Searching Algorithms

The STL provides linear and binary search algorithms, each suited to different scenarios.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

int main() {
    // --- find: linear search, returns iterator ---
    cout << "=== std::find ===" << endl;
    vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};

    auto it = find(v.begin(), v.end(), 9);
    if (it != v.end()) {
        cout << "Found 9 at index: " << distance(v.begin(), it) << endl;
    }

    auto it2 = find(v.begin(), v.end(), 42);
    cout << "Found 42: " << (it2 != v.end() ? "Yes" : "No") << endl;

    // --- find_if: linear search with predicate ---
    cout << "\n=== std::find_if ===" << endl;
    auto firstEven = find_if(v.begin(), v.end(),
                             [](int n){ return n % 2 == 0; });
    if (firstEven != v.end()) {
        cout << "First even number: " << *firstEven
             << " at index " << distance(v.begin(), firstEven) << endl;
    }

    auto firstBig = find_if(v.begin(), v.end(),
                            [](int n){ return n > 7; });
    cout << "First number > 7: " << *firstBig << endl;

    // --- find_if_not: find first element NOT matching predicate ---
    cout << "\n=== std::find_if_not ===" << endl;
    vector<int> v2 = {2, 4, 6, 7, 8, 10};
    auto firstOdd = find_if_not(v2.begin(), v2.end(),
                                [](int n){ return n % 2 == 0; });
    cout << "First non-even: " << *firstOdd << endl;

    // --- binary_search: O(log n) — requires sorted range ---
    cout << "\n=== std::binary_search ===" << endl;
    vector<int> sorted = {1, 3, 5, 7, 9, 11, 13, 15};
    cout << "Searching sorted: ";
    for (int n : sorted) cout << n << " ";
    cout << endl;

    cout << "Contains 7?  " << (binary_search(sorted.begin(), sorted.end(), 7) ? "Yes" : "No") << endl;
    cout << "Contains 6?  " << (binary_search(sorted.begin(), sorted.end(), 6) ? "Yes" : "No") << endl;

    // --- lower_bound / upper_bound ---
    cout << "\n=== lower_bound and upper_bound ===" << endl;
    vector<int> data = {1, 2, 2, 3, 3, 3, 4, 5};
    cout << "Data: ";
    for (int n : data) cout << n << " ";
    cout << endl;

    auto lb = lower_bound(data.begin(), data.end(), 3);
    auto ub = upper_bound(data.begin(), data.end(), 3);
    cout << "lower_bound(3) index: " << distance(data.begin(), lb)
         << " (value: " << *lb << ")" << endl;
    cout << "upper_bound(3) index: " << distance(data.begin(), ub)
         << " (value: " << *ub << ")" << endl;
    cout << "Count of 3s: " << distance(lb, ub) << endl;

    // --- search: find subsequence ---
    cout << "\n=== std::search (subsequence) ===" << endl;
    vector<int> haystack = {1, 2, 3, 4, 5, 3, 4, 5, 6};
    vector<int> needle   = {3, 4, 5};
    auto pos = search(haystack.begin(), haystack.end(),
                      needle.begin(), needle.end());
    if (pos != haystack.end()) {
        cout << "Subsequence {3,4,5} found at index: "
             << distance(haystack.begin(), pos) << endl;
    }

    // --- adjacent_find: find consecutive duplicates ---
    cout << "\n=== std::adjacent_find ===" << endl;
    vector<int> withDups = {1, 2, 3, 3, 4, 5, 5, 6};
    auto dup = adjacent_find(withDups.begin(), withDups.end());
    if (dup != withDups.end()) {
        cout << "First consecutive duplicate: " << *dup
             << " at index " << distance(withDups.begin(), dup) << endl;
    }

    return 0;
}

Step-by-step explanation:

  1. find(begin, end, value): Linear O(n) search — works on unsorted ranges
  2. Returns iterator: Points to first occurrence, or end() if not found
  3. distance(begin, it): Computes index by counting steps from begin to iterator
  4. find_if(begin, end, pred): Finds first element satisfying the predicate
  5. Lambda predicate: [](int n){ return n % 2 == 0; } — returns true for even numbers
  6. First match only: find_if stops at the first matching element
  7. find_if_not: Opposite of find_if — finds first element that does NOT match predicate
  8. binary_search(begin, end, val): O(log n) — REQUIRES sorted input
  9. Returns bool only: Doesn’t tell you where — use lower_bound if you need position
  10. lower_bound(begin, end, val): First position where val could be inserted and keep order — first >= val
  11. upper_bound(begin, end, val): First position after all occurrences of val — first > val
  12. Count occurrences: distance(lower_bound, upper_bound) gives count of equal elements
  13. search(begin, end, sbegin, send): Finds first occurrence of sub-sequence in range — O(n*m)
  14. adjacent_find: Finds first pair of consecutive equal elements — useful for detecting runs

Output:

Plaintext
=== std::find ===
Found 9 at index: 5
Found 42: No

=== std::find_if ===
First even number: 4 at index 2
First number > 7: 9

=== std::find_if_not ===
First non-even: 7

=== std::binary_search ===
Searching sorted: 1 3 5 7 9 11 13 15
Contains 7?  Yes
Contains 6?  No

=== lower_bound and upper_bound ===
Data: 1 2 2 3 3 3 4 5
lower_bound(3) index: 3 (value: 3)
upper_bound(3) index: 6 (value: 4)
Count of 3s: 3

=== std::search (subsequence) ===
Subsequence {3,4,5} found at index: 2

=== std::adjacent_find ===
First consecutive duplicate: 3 at index 2

Transformation Algorithms

Transformation algorithms apply operations to elements, writing results to an output range.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cctype>
using namespace std;

int main() {
    // --- transform: apply function to each element ---
    cout << "=== std::transform (unary) ===" << endl;
    vector<int> v = {1, 2, 3, 4, 5};

    vector<int> squares(v.size());
    transform(v.begin(), v.end(), squares.begin(),
              [](int n){ return n * n; });
    cout << "Squares: ";
    for (int n : squares) cout << n << " ";
    cout << endl;

    // Transform in-place (output == input)
    transform(v.begin(), v.end(), v.begin(),
              [](int n){ return n * 2; });
    cout << "Doubled in-place: ";
    for (int n : v) cout << n << " ";
    cout << endl;

    // --- transform: binary version (two input ranges) ---
    cout << "\n=== std::transform (binary) ===" << endl;
    vector<int> a = {1, 2, 3, 4, 5};
    vector<int> b = {10, 20, 30, 40, 50};
    vector<int> sums(a.size());

    transform(a.begin(), a.end(), b.begin(), sums.begin(),
              [](int x, int y){ return x + y; });
    cout << "Element-wise sums: ";
    for (int n : sums) cout << n << " ";
    cout << endl;

    // --- copy: copy elements to another range ---
    cout << "\n=== std::copy ===" << endl;
    vector<int> src = {10, 20, 30, 40, 50};
    vector<int> dst(src.size());
    copy(src.begin(), src.end(), dst.begin());
    cout << "Copied: ";
    for (int n : dst) cout << n << " ";
    cout << endl;

    // Copy to output stream
    cout << "Copy to cout: ";
    copy(src.begin(), src.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    // --- copy_if: conditional copy ---
    cout << "\n=== std::copy_if ===" << endl;
    vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    vector<int> evens;
    copy_if(nums.begin(), nums.end(), back_inserter(evens),
            [](int n){ return n % 2 == 0; });
    cout << "Even numbers: ";
    for (int n : evens) cout << n << " ";
    cout << endl;

    // --- copy_n: copy exactly n elements ---
    cout << "\n=== std::copy_n ===" << endl;
    vector<int> first3;
    copy_n(nums.begin(), 3, back_inserter(first3));
    cout << "First 3 elements: ";
    for (int n : first3) cout << n << " ";
    cout << endl;

    // --- replace / replace_if ---
    cout << "\n=== std::replace and replace_if ===" << endl;
    vector<int> r = {1, 2, 3, 2, 4, 2, 5};
    replace(r.begin(), r.end(), 2, 99);    // Replace all 2s with 99
    cout << "After replace(2, 99): ";
    for (int n : r) cout << n << " ";
    cout << endl;

    vector<int> r2 = {1, 2, 3, 4, 5, 6, 7, 8};
    replace_if(r2.begin(), r2.end(),
               [](int n){ return n % 2 == 0; }, 0);  // Replace evens with 0
    cout << "After replace_if (evens→0): ";
    for (int n : r2) cout << n << " ";
    cout << endl;

    // --- fill and fill_n ---
    cout << "\n=== std::fill and fill_n ===" << endl;
    vector<int> filled(8);
    fill(filled.begin(), filled.end(), 7);
    cout << "After fill(7): ";
    for (int n : filled) cout << n << " ";
    cout << endl;

    fill_n(filled.begin(), 4, 0);          // Fill only first 4 positions
    cout << "After fill_n(4, 0): ";
    for (int n : filled) cout << n << " ";
    cout << endl;

    // --- String transformation ---
    cout << "\n=== String transform ===" << endl;
    string text = "Hello, World!";
    string upper = text;
    transform(upper.begin(), upper.end(), upper.begin(),
              [](char c){ return toupper(c); });
    cout << "Uppercase: " << upper << endl;

    string lower = text;
    transform(lower.begin(), lower.end(), lower.begin(),
              [](char c){ return tolower(c); });
    cout << "Lowercase: " << lower << endl;

    return 0;
}

Step-by-step explanation:

  1. transform(begin, end, outBegin, func): Applies func to each element, writes result to output
  2. Output range must have space: vector<int> squares(v.size()) pre-sizes output
  3. In-place transform: Using v.begin() as both input and output — modifies elements directly
  4. Binary transform: Takes two input ranges and one output — combines element pairs
  5. back_inserter vs pre-sized: Use back_inserter when output size unknown; pre-size when known
  6. copy(begin, end, outBegin): Copies all elements — equivalent to assignment but works on ranges
  7. ostream_iterator<int>(cout, ” “): Iterator that writes each element to cout with space separator
  8. copy to stream: Elegant way to print container contents without explicit loop
  9. copy_if(begin, end, out, pred): Only copies elements satisfying predicate
  10. copy_n(begin, n, out): Copies exactly n elements — clearer intent than begin+n idiom
  11. replace(begin, end, old, new): Replaces all occurrences of old value with new
  12. replace_if(begin, end, pred, new): Replaces elements satisfying predicate
  13. fill(begin, end, val): Sets every element in range to val — O(n)
  14. fill_n(begin, n, val): Sets exactly n elements starting at begin
  15. toupper/tolower: Standard C functions — wrap in lambda for transform compatibility

Output:

Plaintext
=== std::transform (unary) ===
Squares: 1 4 9 16 25
Doubled in-place: 2 4 6 8 10

=== std::transform (binary) ===
Element-wise sums: 11 22 33 44 55

=== std::copy ===
Copied: 10 20 30 40 50
Copy to cout: 10 20 30 40 50

=== std::copy_if ===
Even numbers: 2 4 6 8 10

=== std::copy_n ===
First 3 elements: 1 2 3

=== std::replace and replace_if ===
After replace(2, 99): 1 99 3 99 4 99 5
After replace_if (evens→0): 1 0 3 0 5 0 7 0

=== std::fill and fill_n ===
After fill(7): 7 7 7 7 7 7 7 7
After fill_n(4, 0): 0 0 0 0 7 7 7 7

=== String transform ===
Uppercase: HELLO, WORLD!
Lowercase: hello, world!

Counting and Aggregation Algorithms

These algorithms reduce a range to a single result: a count, sum, product, or other aggregate.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
using namespace std;

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

    // --- count and count_if ---
    cout << "=== std::count and count_if ===" << endl;
    cout << "Data: ";
    for (int n : v) cout << n << " ";
    cout << endl;

    int count5 = count(v.begin(), v.end(), 5);
    cout << "Count of 5: " << count5 << endl;

    int countBig = count_if(v.begin(), v.end(), [](int n){ return n > 4; });
    cout << "Count of elements > 4: " << countBig << endl;

    int countEven = count_if(v.begin(), v.end(), [](int n){ return n % 2 == 0; });
    cout << "Count of even numbers: " << countEven << endl;

    // --- accumulate: sum / fold ---
    cout << "\n=== std::accumulate ===" << endl;
    int sum = accumulate(v.begin(), v.end(), 0);
    cout << "Sum: " << sum << endl;

    int product = accumulate(v.begin(), v.end(), 1,
                             [](int acc, int n){ return acc * n; });
    cout << "Product: " << product << endl;

    // String concatenation with accumulate
    vector<string> words = {"Hello", " ", "World", "!"};
    string sentence = accumulate(words.begin(), words.end(), string(""));
    cout << "Concatenated: " << sentence << endl;

    // --- min_element / max_element ---
    cout << "\n=== min_element and max_element ===" << endl;
    auto minIt = min_element(v.begin(), v.end());
    auto maxIt = max_element(v.begin(), v.end());
    cout << "Min: " << *minIt << " at index " << distance(v.begin(), minIt) << endl;
    cout << "Max: " << *maxIt << " at index " << distance(v.begin(), maxIt) << endl;

    // minmax_element: finds both in one pass
    auto [lo, hi] = minmax_element(v.begin(), v.end());
    cout << "Min=" << *lo << ", Max=" << *hi << " (single pass)" << endl;

    // --- all_of / any_of / none_of ---
    cout << "\n=== all_of, any_of, none_of ===" << endl;
    vector<int> positives = {1, 2, 3, 4, 5};
    vector<int> mixed     = {-1, 2, -3, 4, 5};
    vector<int> negatives = {-1, -2, -3, -4, -5};

    auto isPositive = [](int n){ return n > 0; };

    cout << "positives — all positive?  " << (all_of(positives.begin(), positives.end(), isPositive) ? "Yes" : "No") << endl;
    cout << "mixed     — any positive?  " << (any_of(mixed.begin(), mixed.end(), isPositive) ? "Yes" : "No") << endl;
    cout << "negatives — none positive? " << (none_of(negatives.begin(), negatives.end(), isPositive) ? "Yes" : "No") << endl;

    // --- iota: fill with sequential values ---
    cout << "\n=== std::iota ===" << endl;
    vector<int> seq(10);
    iota(seq.begin(), seq.end(), 1);     // Fill with 1, 2, 3, ..., 10
    cout << "Sequence 1–10: ";
    for (int n : seq) cout << n << " ";
    cout << endl;

    iota(seq.begin(), seq.end(), 0);     // Fill with 0, 1, 2, ..., 9
    cout << "Sequence 0–9:  ";
    for (int n : seq) cout << n << " ";
    cout << endl;

    return 0;
}

Step-by-step explanation:

  1. count(begin, end, val): Counts occurrences of val using == comparison — O(n)
  2. count_if(begin, end, pred): Counts elements satisfying predicate — O(n)
  3. Multiple count_if calls: Can compute different statistics without multiple loops
  4. accumulate(begin, end, init): Folds range left-to-right: result = init + e[0] + e[1] + …
  5. Initial value matters: Sum starts at 0; product starts at 1 (identity elements)
  6. Custom accumulate operation: Lambda (acc, n) -> acc * n computes running product
  7. String accumulate: Initial value must be string("") not "" — type must match
  8. min_element / max_element: O(n) linear scan — return iterators, not values
  9. Dereference for value: *minIt gives the minimum value; iterator gives position
  10. distance() for index: Computes number of steps from begin to iterator
  11. minmax_element: Returns pair of iterators to min and max in a single O(n) pass
  12. all_of: Returns true if predicate is true for ALL elements — short-circuits on first false
  13. any_of: Returns true if predicate is true for ANY element — short-circuits on first true
  14. none_of: Returns true if predicate is false for ALL elements
  15. iota(begin, end, start): Fills range with start, start+1, start+2, … — from <numeric>

Output:

Plaintext
=== std::count and count_if ===
Data: 3 1 4 1 5 9 2 6 5 3 5
Count of 5: 3
Count of elements > 4: 4
Count of even numbers: 3

=== std::accumulate ===
Sum: 44
Product: 97200
Concatenated: Hello World!

=== min_element and max_element ===
Min: 1 at index 1
Max: 9 at index 5
Min=1, Max=9 (single pass)

=== all_of, any_of, none_of ===
positives — all positive?  Yes
mixed     — any positive?  Yes
negatives — none positive? Yes

=== std::iota ===
Sequence 1–10: 1 2 3 4 5 6 7 8 9 10
Sequence 0–9:  0 1 2 3 4 5 6 7 8 9

Reordering Algorithms

These algorithms rearrange elements without changing the container’s size.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

void print(const vector<int>& v, const string& label) {
    cout << label << ": ";
    for (int n : v) cout << n << " ";
    cout << endl;
}

int main() {
    // --- reverse ---
    cout << "=== std::reverse ===" << endl;
    vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    print(v, "Original");
    reverse(v.begin(), v.end());
    print(v, "Reversed");

    // Reverse only a sub-range
    reverse(v.begin() + 2, v.begin() + 6);
    print(v, "Sub-range reversed [2,6)");

    // --- rotate ---
    cout << "\n=== std::rotate ===" << endl;
    vector<int> r = {1, 2, 3, 4, 5, 6, 7};
    print(r, "Original");
    rotate(r.begin(), r.begin() + 3, r.end());  // Rotate left by 3
    print(r, "After left-rotate by 3");

    rotate(r.begin(), r.end() - 2, r.end());     // Rotate right by 2
    print(r, "After right-rotate by 2");

    // --- shuffle ---
    cout << "\n=== std::shuffle ===" << endl;
    vector<int> deck = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    print(deck, "Before shuffle");
    mt19937 rng(42);   // Mersenne Twister with seed 42
    shuffle(deck.begin(), deck.end(), rng);
    print(deck, "After shuffle");

    // --- unique (remove consecutive duplicates) ---
    cout << "\n=== std::unique ===" << endl;
    vector<int> withDups = {1, 1, 2, 3, 3, 3, 4, 4, 5};
    print(withDups, "Before unique");
    auto newEnd = unique(withDups.begin(), withDups.end());
    withDups.erase(newEnd, withDups.end());
    print(withDups, "After unique + erase");

    // unique works on non-consecutive duplicates only after sort!
    vector<int> scattered = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
    sort(scattered.begin(), scattered.end());
    auto newEnd2 = unique(scattered.begin(), scattered.end());
    scattered.erase(newEnd2, scattered.end());
    print(scattered, "Sorted then deduped");

    // --- partition ---
    cout << "\n=== std::partition ===" << endl;
    vector<int> p = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto pivotIt = partition(p.begin(), p.end(),
                             [](int n){ return n % 2 == 0; });
    cout << "After partition (evens first):" << endl;
    cout << "  Evens: ";
    for (auto it = p.begin(); it != pivotIt; ++it) cout << *it << " ";
    cout << endl;
    cout << "  Odds:  ";
    for (auto it = pivotIt; it != p.end(); ++it) cout << *it << " ";
    cout << endl;

    return 0;
}

Step-by-step explanation:

  1. reverse(begin, end): Reverses elements in-place — O(n), swaps pairs from outside in
  2. Sub-range reverse: Providing narrower iterators reverses only that portion
  3. rotate(begin, newBegin, end): Makes newBegin the first element — wraps around
  4. Left-rotate by 3: Element at index 3 becomes index 0; {1,2,3,4,5,6,7} → {4,5,6,7,1,2,3}
  5. Right-rotate by 2: Use r.end() - 2 as newBegin — last 2 elements move to front
  6. shuffle(begin, end, rng): Randomly reorders elements — requires random number generator
  7. mt19937 rng(42): Mersenne Twister PRNG with seed 42 — reproducible shuffle for testing
  8. shuffle replaces random_shuffle: random_shuffle deprecated in C++17 — always use shuffle
  9. unique(begin, end): Moves consecutive duplicates to end, returns iterator to new logical end
  10. unique only removes consecutive: {1,1,2,3} → {1,2,3}, but {1,2,1} stays {1,2,1}
  11. Sort before unique: Always sort first if you want ALL duplicates removed
  12. Erase-unique idiom: unique() + erase() — two steps required to physically remove
  13. partition(begin, end, pred): Rearranges so all elements satisfying pred come first
  14. Returns pivot iterator: Points to first element NOT satisfying pred — boundary between groups
  15. partition doesn’t sort within groups: Relative order within each group not guaranteed

Output:

Plaintext
=== std::reverse ===
Original: 1 2 3 4 5 6 7 8 9
Reversed: 9 8 7 6 5 4 3 2 1
Sub-range reversed [2,6): 9 8 4 5 6 7 3 2 1

=== std::rotate ===
Original: 1 2 3 4 5 6 7
After left-rotate by 3: 4 5 6 7 1 2 3
After right-rotate by 2: 2 3 4 5 6 7 1

=== std::shuffle ===
Before shuffle: 1 2 3 4 5 6 7 8 9 10
After shuffle:  5 2 8 1 10 3 6 9 4 7

=== std::unique ===
Before unique: 1 1 2 3 3 3 4 4 5
After unique + erase: 1 2 3 4 5
Sorted then deduped: 1 2 3 4 5 6 9

=== std::partition ===
After partition (evens first):
  Evens: 10 2 8 4 6
  Odds:  5 7 3 9 1

Algorithms with Lambdas: Real-World Patterns

Combining algorithms with lambdas enables expressive, readable data-processing pipelines.

C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
using namespace std;

struct Product {
    string name;
    string category;
    double price;
    int quantity;
};

int main() {
    vector<Product> inventory = {
        {"Widget A",  "Electronics", 29.99, 100},
        {"Widget B",  "Clothing",    49.99,  50},
        {"Gadget X",  "Electronics", 99.99,  25},
        {"Shirt M",   "Clothing",    19.99, 200},
        {"Gadget Y",  "Electronics",149.99,  10},
        {"Pants L",   "Clothing",    59.99,  75},
        {"Device Z",  "Electronics", 79.99,  40}
    };

    // --- Find most expensive product ---
    cout << "=== Most Expensive Product ===" << endl;
    auto mostExpensive = max_element(inventory.begin(), inventory.end(),
        [](const Product& a, const Product& b) {
            return a.price < b.price;
        });
    cout << mostExpensive->name << " at $" << mostExpensive->price << endl;

    // --- Total inventory value ---
    cout << "\n=== Total Inventory Value ===" << endl;
    double totalValue = accumulate(inventory.begin(), inventory.end(), 0.0,
        [](double acc, const Product& p) {
            return acc + p.price * p.quantity;
        });
    cout << "Total value: $" << totalValue << endl;

    // --- Filter electronics ---
    cout << "\n=== Electronics Only ===" << endl;
    vector<Product> electronics;
    copy_if(inventory.begin(), inventory.end(), back_inserter(electronics),
        [](const Product& p) { return p.category == "Electronics"; });
    for (const auto& p : electronics) {
        cout << "  " << p.name << " - $" << p.price << endl;
    }

    // --- Sort by price ascending ---
    cout << "\n=== Sorted by Price (Ascending) ===" << endl;
    vector<Product> sorted = inventory;
    sort(sorted.begin(), sorted.end(),
         [](const Product& a, const Product& b) {
             return a.price < b.price;
         });
    for (const auto& p : sorted) {
        cout << "  $" << p.price << " - " << p.name << endl;
    }

    // --- Count low-stock items ---
    cout << "\n=== Low Stock Alert (qty < 30) ===" << endl;
    int lowStock = count_if(inventory.begin(), inventory.end(),
        [](const Product& p) { return p.quantity < 30; });
    cout << "Low stock items: " << lowStock << endl;

    // --- Check if any item is out of stock ---
    bool anyOutOfStock = any_of(inventory.begin(), inventory.end(),
        [](const Product& p) { return p.quantity == 0; });
    cout << "Any out of stock: " << (anyOutOfStock ? "Yes" : "No") << endl;

    // --- Apply 10% discount to electronics ---
    cout << "\n=== After 10% Discount on Electronics ===" << endl;
    for_each(inventory.begin(), inventory.end(),
        [](Product& p) {
            if (p.category == "Electronics") {
                p.price *= 0.9;
            }
        });
    for (const auto& p : inventory) {
        if (p.category == "Electronics") {
            cout << "  " << p.name << " now $" << p.price << endl;
        }
    }

    return 0;
}

Step-by-step explanation:

  1. max_element with custom comparator: Compares Product objects by price field
  2. Lambda accessing struct member: a.price < b.price defines ordering for Product comparison
  3. Returns iterator: Dereference with -> to access struct members
  4. accumulate for complex aggregation: Lambda (acc, product) -> acc + price * quantity
  5. 0.0 as initial value: Double initial value ensures accumulate uses double arithmetic
  6. copy_if for filtering: Creates subset vector without modifying original
  7. back_inserter: Grows electronics vector as elements are copied
  8. Category comparison: String equality check in lambda as filter criterion
  9. Sort copy, not original: vector<Product> sorted = inventory preserves original order
  10. Lambda for custom sort: Any comparison logic wrapped in lambda
  11. count_if for statistics: Counts products with quantity < 30 — low stock detection
  12. any_of for existence check: Returns immediately on first out-of-stock item found
  13. for_each with mutation: Lambda takes Product& (reference) — modifies elements in-place
  14. Conditional modification: Only electronics discounted — condition inside lambda
  15. Algorithms compose: Multiple algorithms on same container build data processing pipeline

Output:

Plaintext
=== Most Expensive Product ===
Gadget Y at $149.99

=== Total Inventory Value ===
Total value: $27597.5

=== Electronics Only ===
  Widget A - $29.99
  Gadget X - $99.99
  Gadget Y - $149.99
  Device Z - $79.99

=== Sorted by Price (Ascending) ===
  $19.99 - Shirt M
  $29.99 - Widget A
  $49.99 - Widget B
  $59.99 - Pants L
  $79.99 - Device Z
  $99.99 - Gadget X
  $149.99 - Gadget Y

=== Low Stock Alert (qty < 30) ===
Low stock items: 2

=== Any out of stock: No

=== After 10% Discount on Electronics ===
  Widget A now $26.991
  Gadget X now $89.991
  Gadget Y now $134.991
  Device Z now $71.991

STL Algorithm Quick Reference

AlgorithmHeaderComplexityDescription
sort<algorithm>O(n log n)Sort range in-place
stable_sort<algorithm>O(n log n)Sort preserving equal element order
partial_sort<algorithm>O(n log k)Sort first k elements
nth_element<algorithm>O(n)Place nth sorted element at position n
find<algorithm>O(n)Find first occurrence of value
find_if<algorithm>O(n)Find first element matching predicate
binary_search<algorithm>O(log n)Check if value exists (sorted range)
lower_bound<algorithm>O(log n)First position >= value (sorted)
upper_bound<algorithm>O(log n)First position > value (sorted)
count<algorithm>O(n)Count occurrences of value
count_if<algorithm>O(n)Count elements matching predicate
transform<algorithm>O(n)Apply function to each element
copy<algorithm>O(n)Copy range to output
copy_if<algorithm>O(n)Conditionally copy elements
replace<algorithm>O(n)Replace all occurrences of value
fill<algorithm>O(n)Set all elements to value
reverse<algorithm>O(n)Reverse range in-place
rotate<algorithm>O(n)Rotate elements around pivot
shuffle<algorithm>O(n)Randomly reorder elements
unique<algorithm>O(n)Remove consecutive duplicates
partition<algorithm>O(n)Rearrange by predicate
accumulate<numeric>O(n)Fold range to single value
min_element<algorithm>O(n)Iterator to minimum element
max_element<algorithm>O(n)Iterator to maximum element
all_of<algorithm>O(n)True if all elements match predicate
any_of<algorithm>O(n)True if any element matches predicate
none_of<algorithm>O(n)True if no element matches predicate
iota<numeric>O(n)Fill with sequentially increasing values
for_each<algorithm>O(n)Apply function to each element

Conclusion: Write Less, Do More with STL Algorithms

The STL algorithm library is one of C++’s greatest strengths. By expressing operations at the level of intent—”find the first element matching this condition,” “transform each element with this function,” “sort by this criterion”—rather than implementation detail (manually written loops), your code becomes shorter, clearer, more correct, and often faster.

Key takeaways:

  • All algorithms operate on iterator ranges [begin, end) — the same pattern works on any compatible container
  • Sorting: Use sort for maximum speed, stable_sort when equal-element order matters, partial_sort for top-K
  • Searching: Use find/find_if for linear search, binary_search/lower_bound/upper_bound for O(log n) on sorted ranges
  • Transforming: transform applies a function, copy_if filters, replace_if conditionally replaces
  • Aggregating: accumulate folds to a value, count_if counts matches, all_of/any_of/none_of test conditions
  • Reordering: reverse, rotate, shuffle, partition, unique + erase
  • Lambdas are the key to customizing algorithm behavior — they replace manual comparison functions and predicates
  • Algorithms compose naturally: filter with copy_if, sort the result, accumulate a total
  • Include <algorithm> for most algorithms and <numeric> for accumulate, iota, and numeric operations

Replace your manual loops with algorithms wherever possible — your code will be shorter, your intent clearer, and your colleagues will thank you.

Share:
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments

Discover More

The Essential Math for Robotics: What You Actually Need to Know

Discover what math you actually need for robotics. Learn which mathematical concepts matter most and…

Why Deep Learning Requires So Much Data

Why Deep Learning Requires So Much Data

Discover why deep learning needs massive datasets, how much data is required, techniques to reduce…

What is Ground in Electronics? Clearing Up a Common Confusion

Demystify the confusing concept of ground in electronics. Learn what ground really means, different types…

Do You Need a PhD to Become a Data Scientist?

Wondering if you need a PhD for data science? Learn the truth about educational requirements,…

What is Machine Learning? Understanding the Learning Process

Discover what machine learning is, how computers learn from data, and explore real-world applications that…

Your First Week in Data Science: A Practical Roadmap

Start your data science journey right with this practical first-week roadmap. Learn what to focus…

Click For More
0
Would love your thoughts, please comment.x
()
x