Before STL algorithms, code looked like this:
for (int i = 0; i < size; ++i) {
if (arr[i] == x) { ... }
}
Problems:
- Logic duplicated everywhere
- Hard to optimize globally
- Tightly coupled to container type
- Error-prone
STL’s Core Idea
Algorithms should not know how data is stored, they should only know how to traverse it.
That is why algorithms accept:
[first_iterator, last_iterator)
STL Algorithm
An STL algorithm is:
- A template function
- Operating on iterator ranges
- Independent of container type
- Optimized based on iterator category
Example:
std::sort(v.begin(), v.end());
std::sort:
- Does not know
vector - Does not know memory layout
- Only requires Random Access Iterators
The Iterator Range Convention
All STL algorithms follow this rule:
[first, last)
Meaning:
first→ includedlast→ excluded
[first] [elem] [elem] [elem] [last)
This allows:
- Empty ranges
- Safe composition
- No off-by-one errors
Categories of STL Algorithms
STL algorithms can be grouped by what they do, not how they store data.
Main categories:
- Non-modifying algorithms
- Modifying algorithms
- Sorting & ordering algorithms
- Searching algorithms
- Numeric algorithms
- Set algorithms
1. Non-Modifying Algorithms
They observe data, but do not change it
`std::find`
auto it = std::find(v.begin(), v.end(), 5);
[1][3][5][7]
^
found
Complexity: O(n)
`std::count`
int c = std::count(v.begin(), v.end(), 2);
Counts occurrences.
`std::any_of`, `all_of`, `none_of`
std::all_of(v.begin(), v.end(), [](int x) {
return x > 0;
});
Use when:
- Validating conditions
- Avoiding manual loops
- Expressing intent clearly
2. Modifying Algorithms
They change elements or rearrange them
`std::copy`
std::copy(src.begin(), src.end(), dest.begin());
src → [1][2][3]
dest → [ ][ ][ ]
Use back_inserter for safety:
std::copy(src.begin(), src.end(), std::back_inserter(dest));
`std::transform`
std::transform(v.begin(), v.end(), v.begin(),
[](int x) { return x * 2; });
Before:
[1][2][3]
After:
[2][4][6]
`std::remove` (VERY IMPORTANT)
v.erase(std::remove(v.begin(), v.end(), 3), v.end());
Why this looks strange:
remove DOES NOT erase
It moves elements, then returns a new logical end.
Before: [1][3][2][3][4]
After : [1][2][4][x][x]
^
new logical end
3. Sorting & Ordering Algorithms
`std::sort`
std::sort(v.begin(), v.end());
Requirements:
- Random Access Iterators
operator<
Complexity: O(n log n)
Custom comparator
std::sort(v.begin(), v.end(),
[](int a, int b) {
return a > b;
});
`std::stable_sort`
Preserves relative order of equal elements.
Use when:
- Order matters semantically
- Slightly slower than
sort
`std::partial_sort`
std::partial_sort(v.begin(), v.begin()+3, v.end());
Use when:
- Only top N elements are needed
- Avoid full sort cost
4. Searching Algorithms
Require sorted ranges
`std::binary_search`
bool found = std::binary_search(v.begin(), v.end(), 10);
Complexity: O(log n)
`std::lower_bound` / `upper_bound`
auto it = std::lower_bound(v.begin(), v.end(), 10);
[1][3][5][7][10][10][12]
^
lower_bound
Used heavily in:
- Maps
- Ranges
- Custom search logic
5. Numeric Algorithms
Located in <numeric>
`std::accumulate`
int sum = std::accumulate(v.begin(), v.end(), 0);
Equivalent to:
int sum = 0;
for (...) sum += x;
`std::inner_product`
int r = std::inner_product(a.begin(), a.end(), b.begin(), 0);
Used in:
- Linear algebra
- Signal processing
- Performance-critical math
6. Set Algorithms
Operate on sorted ranges
`std::set_union`
std::set_union(a.begin(), a.end(),
b.begin(), b.end(),
std::back_inserter(result));
A: [1][3][5]
B: [2][3][4]
Result: [1][2][3][4][5]
Algorithms and Iterator Categories (Performance!)
Algorithms select implementation based on iterator strength.
Example:
std::advance(it, n);
| Iterator Type | Complexity |
|---|---|
| Random Access | O(1) |
| Bidirectional | O(n) |
| Forward | O(n) |
Same algorithm — different performance.
Algorithms vs Member Functions
Example:
std::find(v.begin(), v.end(), x);
v.find(x); // ❌ does not exist
But:
set.find(x); // O(log n)
Rule:
- Generic algorithms → work everywhere
- Container member functions → optimized for structure
Algorithms + Lambdas = Modern C++
std::erase_if(v, [](int x) {
return x % 2 == 0;
});
Benefits:
- Clear intent
- No loops
- Fewer bugs
- Optimizable
When to Use STL Algorithms
Always prefer algorithms when:
- Traversing containers
- Transforming data
- Searching, counting, sorting
- Writing readable, maintainable code
Avoid manual loops when:
- Algorithm already exists
- Logic is standard
- Performance matters