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).
#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:
- #include <algorithm>: Header for most STL algorithms (sort, find, transform, etc.)
- #include <numeric>: Header for numeric algorithms (accumulate, iota, inner_product)
- for_each(begin, end, func): Calls func on each element in the range — simplest algorithm
- v.begin(), v.end(): Full range — all elements
- v.begin() + 2, v.begin() + 7: Sub-range — elements at indices 2 through 6
- Sub-range flexibility: Every algorithm accepts any valid iterator pair — partial processing is easy
- arr, arr + 5: Plain C arrays expose pointer-based “iterators” — algorithms work unchanged
- Container agnosticism: for_each doesn’t know or care whether it’s iterating a vector or array
- accumulate(begin, end, init): Sums all elements starting from initial value 0
- min_element / max_element: Return iterators to min/max element — dereference with *
- Why return iterators?: Lets you know the position, not just the value — useful for further operations
- Universal pattern:
(begin, end, ...)— once you know this pattern, all algorithms feel familiar - Zero runtime overhead: Algorithms compile to the same code as hand-written loops
Output:
=== 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: 9Sorting Algorithms
Sorting is one of the most fundamental operations. The STL provides several variants for different needs.
#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:
- sort(begin, end): Default ascending sort — uses introsort (hybrid quicksort/heapsort) — O(n log n)
- sort is NOT stable: Equal elements may reorder relative to each other
- greater<int>(): Standard comparator functor for descending order
- Custom lambda comparator: Returns true when a should come before b
- Odd-first comparator:
(a % 2 == 0) < (b % 2 == 0)— false(odd) < true(even) → odds first - stable_sort: Same O(n log n) but guarantees equal elements keep their original relative order
- When stable matters: Alice and Carol both age 30 — stable_sort preserves Alice before Carol
- Diana and Bob both 25: Bob was first, so stays before Diana after stable sort by age
- partial_sort(begin, middle, end): Sorts only first (middle-begin) elements — O(n log k)
- Use case: Finding top-K without sorting everything — much faster for small k, large n
- nth_element: Rearranges so nth position has element it would have if fully sorted — O(n)
- Elements before nth: All smaller than or equal to *nth (not necessarily sorted)
- Elements after nth: All greater than or equal to *nth (not necessarily sorted)
- Median finding: nth_element at midpoint finds median without full sort — O(n) vs O(n log n)
- is_sorted: O(n) check — returns true if range is already in non-decreasing order
Output:
=== 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: NoSearching Algorithms
The STL provides linear and binary search algorithms, each suited to different scenarios.
#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:
- find(begin, end, value): Linear O(n) search — works on unsorted ranges
- Returns iterator: Points to first occurrence, or end() if not found
- distance(begin, it): Computes index by counting steps from begin to iterator
- find_if(begin, end, pred): Finds first element satisfying the predicate
- Lambda predicate:
[](int n){ return n % 2 == 0; }— returns true for even numbers - First match only: find_if stops at the first matching element
- find_if_not: Opposite of find_if — finds first element that does NOT match predicate
- binary_search(begin, end, val): O(log n) — REQUIRES sorted input
- Returns bool only: Doesn’t tell you where — use lower_bound if you need position
- lower_bound(begin, end, val): First position where val could be inserted and keep order — first >= val
- upper_bound(begin, end, val): First position after all occurrences of val — first > val
- Count occurrences:
distance(lower_bound, upper_bound)gives count of equal elements - search(begin, end, sbegin, send): Finds first occurrence of sub-sequence in range — O(n*m)
- adjacent_find: Finds first pair of consecutive equal elements — useful for detecting runs
Output:
=== 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 2Transformation Algorithms
Transformation algorithms apply operations to elements, writing results to an output range.
#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:
- transform(begin, end, outBegin, func): Applies func to each element, writes result to output
- Output range must have space:
vector<int> squares(v.size())pre-sizes output - In-place transform: Using v.begin() as both input and output — modifies elements directly
- Binary transform: Takes two input ranges and one output — combines element pairs
- back_inserter vs pre-sized: Use back_inserter when output size unknown; pre-size when known
- copy(begin, end, outBegin): Copies all elements — equivalent to assignment but works on ranges
- ostream_iterator<int>(cout, ” “): Iterator that writes each element to cout with space separator
- copy to stream: Elegant way to print container contents without explicit loop
- copy_if(begin, end, out, pred): Only copies elements satisfying predicate
- copy_n(begin, n, out): Copies exactly n elements — clearer intent than begin+n idiom
- replace(begin, end, old, new): Replaces all occurrences of old value with new
- replace_if(begin, end, pred, new): Replaces elements satisfying predicate
- fill(begin, end, val): Sets every element in range to val — O(n)
- fill_n(begin, n, val): Sets exactly n elements starting at begin
- toupper/tolower: Standard C functions — wrap in lambda for transform compatibility
Output:
=== 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.
#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:
- count(begin, end, val): Counts occurrences of val using == comparison — O(n)
- count_if(begin, end, pred): Counts elements satisfying predicate — O(n)
- Multiple count_if calls: Can compute different statistics without multiple loops
- accumulate(begin, end, init): Folds range left-to-right: result = init + e[0] + e[1] + …
- Initial value matters: Sum starts at 0; product starts at 1 (identity elements)
- Custom accumulate operation: Lambda
(acc, n) -> acc * ncomputes running product - String accumulate: Initial value must be
string("")not""— type must match - min_element / max_element: O(n) linear scan — return iterators, not values
- Dereference for value: *minIt gives the minimum value; iterator gives position
- distance() for index: Computes number of steps from begin to iterator
- minmax_element: Returns pair of iterators to min and max in a single O(n) pass
- all_of: Returns true if predicate is true for ALL elements — short-circuits on first false
- any_of: Returns true if predicate is true for ANY element — short-circuits on first true
- none_of: Returns true if predicate is false for ALL elements
- iota(begin, end, start): Fills range with start, start+1, start+2, … — from
<numeric>
Output:
=== 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 9Reordering Algorithms
These algorithms rearrange elements without changing the container’s size.
#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:
- reverse(begin, end): Reverses elements in-place — O(n), swaps pairs from outside in
- Sub-range reverse: Providing narrower iterators reverses only that portion
- rotate(begin, newBegin, end): Makes newBegin the first element — wraps around
- 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}
- Right-rotate by 2: Use
r.end() - 2as newBegin — last 2 elements move to front - shuffle(begin, end, rng): Randomly reorders elements — requires random number generator
- mt19937 rng(42): Mersenne Twister PRNG with seed 42 — reproducible shuffle for testing
- shuffle replaces random_shuffle: random_shuffle deprecated in C++17 — always use shuffle
- unique(begin, end): Moves consecutive duplicates to end, returns iterator to new logical end
- unique only removes consecutive: {1,1,2,3} → {1,2,3}, but {1,2,1} stays {1,2,1}
- Sort before unique: Always sort first if you want ALL duplicates removed
- Erase-unique idiom:
unique()+erase()— two steps required to physically remove - partition(begin, end, pred): Rearranges so all elements satisfying pred come first
- Returns pivot iterator: Points to first element NOT satisfying pred — boundary between groups
- partition doesn’t sort within groups: Relative order within each group not guaranteed
Output:
=== 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 1Algorithms with Lambdas: Real-World Patterns
Combining algorithms with lambdas enables expressive, readable data-processing pipelines.
#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:
- max_element with custom comparator: Compares Product objects by price field
- Lambda accessing struct member:
a.price < b.pricedefines ordering for Product comparison - Returns iterator: Dereference with -> to access struct members
- accumulate for complex aggregation: Lambda
(acc, product) -> acc + price * quantity - 0.0 as initial value: Double initial value ensures accumulate uses double arithmetic
- copy_if for filtering: Creates subset vector without modifying original
- back_inserter: Grows electronics vector as elements are copied
- Category comparison: String equality check in lambda as filter criterion
- Sort copy, not original:
vector<Product> sorted = inventorypreserves original order - Lambda for custom sort: Any comparison logic wrapped in lambda
- count_if for statistics: Counts products with quantity < 30 — low stock detection
- any_of for existence check: Returns immediately on first out-of-stock item found
- for_each with mutation: Lambda takes
Product&(reference) — modifies elements in-place - Conditional modification: Only electronics discounted — condition inside lambda
- Algorithms compose: Multiple algorithms on same container build data processing pipeline
Output:
=== 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.991STL Algorithm Quick Reference
| Algorithm | Header | Complexity | Description |
|---|---|---|---|
| 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
sortfor maximum speed,stable_sortwhen equal-element order matters,partial_sortfor top-K - Searching: Use
find/find_iffor linear search,binary_search/lower_bound/upper_boundfor O(log n) on sorted ranges - Transforming:
transformapplies a function,copy_iffilters,replace_ifconditionally replaces - Aggregating:
accumulatefolds to a value,count_ifcounts matches,all_of/any_of/none_oftest 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.








