CODE: CPP STL Algotithms

STL algorithms are template functions that operate on iterator ranges, not on containers themselves. If templates are the engine of C++, and containers are the data structures, then algorithms are the actual work being done.

Before STL algorithms, code looked like this:

cpp
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:

cpp
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 → included
  • last → excluded

txt
[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:

  1. Non-modifying algorithms
  2. Modifying algorithms
  3. Sorting & ordering algorithms
  4. Searching algorithms
  5. Numeric algorithms
  6. Set algorithms

1. Non-Modifying Algorithms

They observe data, but do not change it

`std::find`

cpp
auto it = std::find(v.begin(), v.end(), 5);

txt
[1][3][5][7]
        ^
       found

Complexity: O(n)


`std::count`

cpp
int c = std::count(v.begin(), v.end(), 2);

Counts occurrences.


`std::any_of`, `all_of`, `none_of`

cpp
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`

cpp
std::copy(src.begin(), src.end(), dest.begin());

txt
src  → [1][2][3]
dest → [ ][ ][ ]

Use back_inserter for safety:

cpp
std::copy(src.begin(), src.end(), std::back_inserter(dest));


`std::transform`

cpp
std::transform(v.begin(), v.end(), v.begin(),
               [](int x) { return x * 2; });

Before:

txt
[1][2][3]

After:

txt
[2][4][6]


`std::remove` (VERY IMPORTANT)

cpp
v.erase(std::remove(v.begin(), v.end(), 3), v.end());

Why this looks strange:

txt
remove DOES NOT erase

It moves elements, then returns a new logical end.

txt
Before: [1][3][2][3][4]
After : [1][2][4][x][x]
                 ^
             new logical end


3. Sorting & Ordering Algorithms

`std::sort`

cpp
std::sort(v.begin(), v.end());

Requirements:

  • Random Access Iterators
  • operator<

Complexity: O(n log n)

Custom comparator

cpp
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`

cpp
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

cpp
bool found = std::binary_search(v.begin(), v.end(), 10);

Complexity: O(log n)


`std::lower_bound` / `upper_bound`

cpp
auto it = std::lower_bound(v.begin(), v.end(), 10);

txt
[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`

cpp
int sum = std::accumulate(v.begin(), v.end(), 0);

Equivalent to:

cpp
int sum = 0;
for (...) sum += x;


`std::inner_product`

cpp
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`

cpp
std::set_union(a.begin(), a.end(),
               b.begin(), b.end(),
               std::back_inserter(result));

txt
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:

cpp
std::advance(it, n);

Iterator TypeComplexity
Random AccessO(1)
BidirectionalO(n)
ForwardO(n)

Same algorithm — different performance.


Algorithms vs Member Functions

Example:

cpp
std::find(v.begin(), v.end(), x);
v.find(x); // ❌ does not exist

But:

cpp
set.find(x); // O(log n)

Rule:

  • Generic algorithms → work everywhere
  • Container member functions → optimized for structure

Algorithms + Lambdas = Modern C++

cpp
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