STL Algorithms
The <algorithm> and <numeric> headers provide over 100 generic algorithms that work on any range defined by iterators. These algorithms are composable, well-tested, and often more efficient than hand-written loops. Writing sort(v.begin(), v.end()) is clearer, shorter, and less error-prone than implementing quicksort yourself.
The Algorithm Pattern
All STL algorithms operate on iterator ranges — a begin and end iterator that define the sequence:
cpp
#include <algorithm>
#include <vector>
using namespace std;
vector<int> v = {5, 2, 8, 1, 9, 3};
// [begin, end) — includes begin, excludes end
sort(v.begin(), v.end()); // Entire vector
sort(v.begin(), v.begin() + 3); // First 3 elements only
sort(v.begin(), v.end(), greater<int>()); // DescendingSearching Algorithms
cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
// find — returns iterator to first match or end
auto it = find(v.begin(), v.end(), 9);
cout << (it != v.end() ? "Found 9" : "Not found") << "\n"; // Found 9
// find_if — find first element matching predicate
auto it2 = find_if(v.begin(), v.end(), [](int x) { return x > 7; });
cout << "First > 7: " << *it2 << "\n"; // 9
// count / count_if
cout << count(v.begin(), v.end(), 1) << "\n"; // 2 (two 1s)
cout << count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }) << "\n"; // 3
// any_of, all_of, none_of
cout << any_of(v.begin(), v.end(), [](int x) { return x > 8; }) << "\n"; // 1
cout << all_of(v.begin(), v.end(), [](int x) { return x > 0; }) << "\n"; // 1
cout << none_of(v.begin(), v.end(), [](int x) { return x < 0; }) << "\n"; // 1
// Binary search (sorted range required)
vector<int> sorted = {1, 2, 3, 4, 5, 6, 7, 8, 9};
cout << binary_search(sorted.begin(), sorted.end(), 7) << "\n"; // 1
// lower_bound / upper_bound
auto lb = lower_bound(sorted.begin(), sorted.end(), 5); // First >= 5
auto ub = upper_bound(sorted.begin(), sorted.end(), 5); // First > 5
cout << *lb << "\n"; // 5
cout << *ub << "\n"; // 6
return 0;
}Sorting
cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
struct Person {
string name;
int age;
};
int main() {
vector<int> v = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// Ascending (default)
sort(v.begin(), v.end());
// Descending
sort(v.begin(), v.end(), greater<int>());
// Partial sort — only sort the 3 smallest to front
vector<int> v2 = {5, 2, 8, 1, 9, 3};
partial_sort(v2.begin(), v2.begin() + 3, v2.end());
// v2[0..2] = {1, 2, 3} (sorted), rest is unspecified
// Stable sort — preserves relative order of equal elements
vector<Person> people = {{"Alice", 25}, {"Bob", 25}, {"Charlie", 30}};
stable_sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
for (const auto& p : people) cout << p.name << " ";
cout << "\n"; // Alice Bob Charlie (Alice before Bob — stable)
// nth_element — O(n) partial sort; ensures correct element at position n
vector<int> v3 = {5, 2, 8, 1, 9, 3, 7};
nth_element(v3.begin(), v3.begin() + 3, v3.end());
cout << "4th smallest: " << v3[3] << "\n"; // 5
// is_sorted
cout << is_sorted(v.begin(), v.end()) << "\n"; // 1 (true — descending sorted)
return 0;
}Transforming Elements
cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <cmath>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
// transform — apply function to each element
vector<int> squared(v.size());
transform(v.begin(), v.end(), squared.begin(),
[](int x) { return x * x; });
// squared = {1, 4, 9, 16, 25}
// transform two ranges
vector<int> a = {1, 2, 3}, b = {10, 20, 30};
vector<int> sums(3);
transform(a.begin(), a.end(), b.begin(), sums.begin(),
[](int x, int y) { return x + y; });
for (int n : sums) cout << n << " ";
cout << "\n"; // 11 22 33
// for_each — apply void function to each element
vector<double> vals = {1.0, 4.0, 9.0, 16.0, 25.0};
for_each(vals.begin(), vals.end(), [](double& x) { x = sqrt(x); });
for (double d : vals) cout << d << " ";
cout << "\n"; // 1 2 3 4 5
// replace / replace_if
vector<int> w = {1, 2, 0, 4, 0, 6};
replace(w.begin(), w.end(), 0, -1); // Replace 0 with -1
replace_if(w.begin(), w.end(), [](int x) { return x < 0; }, 0); // Restore
return 0;
}Accumulation and Reduction
cpp
#include <iostream>
#include <numeric>
#include <vector>
#include <functional>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
// accumulate — reduce to single value
int sum = accumulate(v.begin(), v.end(), 0);
int product = accumulate(v.begin(), v.end(), 1, multiplies<int>());
cout << sum << "\n"; // 15
cout << product << "\n"; // 120
// iota — fill with increasing values
vector<int> range(10);
iota(range.begin(), range.end(), 1); // {1,2,3,...,10}
// inner_product — dot product
vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
int dot = inner_product(a.begin(), a.end(), b.begin(), 0);
cout << dot << "\n"; // 4+10+18 = 32
// partial_sum — running sum
vector<int> prefix(v.size());
partial_sum(v.begin(), v.end(), prefix.begin());
for (int n : prefix) cout << n << " ";
cout << "\n"; // 1 3 6 10 15
return 0;
}Removing Elements
cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 2, 4, 2, 5};
// IMPORTANT: remove() doesn't resize — it moves elements to back
// Must follow with erase() — the "erase-remove idiom"
auto newEnd = remove(v.begin(), v.end(), 2);
v.erase(newEnd, v.end());
// v = {1, 3, 4, 5}
for (int n : v) cout << n << " ";
cout << "\n";
// remove_if
vector<int> w = {1, 2, 3, 4, 5, 6, 7, 8};
w.erase(remove_if(w.begin(), w.end(), [](int x) { return x % 2 == 0; }),
w.end());
for (int n : w) cout << n << " ";
cout << "\n"; // 1 3 5 7
// unique — remove consecutive duplicates (sort first for all duplicates)
vector<int> u = {1, 1, 2, 2, 3, 1, 1};
sort(u.begin(), u.end()); // Sort first
u.erase(unique(u.begin(), u.end()), u.end());
for (int n : u) cout << n << " ";
cout << "\n"; // 1 2 3
return 0;
}Partition and Set Operations
cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
// partition — rearrange so predicate-true elements come first
vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
auto pivot = partition(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
// Even elements before odd — order within each group not guaranteed
// stable_partition — preserves relative order within each group
vector<int> w = {1, 2, 3, 4, 5, 6};
stable_partition(w.begin(), w.end(), [](int x) { return x > 3; });
for (int n : w) cout << n << " ";
cout << "\n"; // 4 5 6 1 2 3
// Set operations (on sorted ranges)
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b = {3, 4, 5, 6, 7};
vector<int> inter, uni, diff;
set_intersection(a.begin(), a.end(), b.begin(), b.end(),
back_inserter(inter));
set_union(a.begin(), a.end(), b.begin(), b.end(),
back_inserter(uni));
set_difference(a.begin(), a.end(), b.begin(), b.end(),
back_inserter(diff));
for (int n : inter) cout << n << " "; // 3 4 5
cout << "\n";
for (int n : diff) cout << n << " "; // 1 2 (in a but not b)
cout << "\n";
return 0;
}Practical Example: Data Pipeline
cpp
#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <string>
#include <iomanip>
using namespace std;
struct Sale {
string product;
int quantity;
double unitPrice;
double total() const { return quantity * unitPrice; }
};
int main() {
vector<Sale> sales = {
{"Laptop", 5, 45000.0},
{"Mouse", 20, 999.0},
{"Keyboard", 8, 1499.0},
{"Monitor", 3, 18000.0},
{"Headset", 12, 2499.0},
{"Webcam", 6, 2999.0},
};
// 1. Filter: Only high-value sales (total > 10,000)
vector<Sale> highValue;
copy_if(sales.begin(), sales.end(), back_inserter(highValue),
[](const Sale& s) { return s.total() > 10000; });
// 2. Sort by total descending
sort(highValue.begin(), highValue.end(),
[](const Sale& a, const Sale& b) { return a.total() > b.total(); });
// 3. Print top sales
cout << "Top Sales (>₹10,000):\n";
cout << string(45, '-') << "\n";
for (const auto& s : highValue) {
cout << left << setw(12) << s.product
<< " qty=" << setw(3) << s.quantity
<< " total=₹" << fixed << setprecision(0) << s.total() << "\n";
}
// 4. Total revenue from filtered sales
double revenue = accumulate(highValue.begin(), highValue.end(), 0.0,
[](double acc, const Sale& s) {
return acc + s.total();
});
cout << "\nRevenue from top sales: ₹" << revenue << "\n";
// 5. Max single product revenue
auto maxIt = max_element(sales.begin(), sales.end(),
[](const Sale& a, const Sale& b) {
return a.total() < b.total();
});
cout << "Best product: " << maxIt->product << " (₹" << maxIt->total() << ")\n";
return 0;
}Summary
- STL algorithms work on iterator ranges —
(begin, end)— and work with any container sort,stable_sort,partial_sort,nth_element— sorting familyfind,find_if,count_if,any_of,all_of— searching familytransform,for_each,replace_if— transformation familyaccumulate,inner_product,partial_sum— reduction family (in<numeric>)remove_if+erase(the erase-remove idiom) — removal family- Always use
back_inserter(v)as output iterator when you want to push to a vector - Lambdas as comparators and predicates are the modern way to customise algorithm behaviour
Next, we'll explore iterators — the abstraction that connects containers to algorithms, and how to write your own.