Intermediate 11 min

Algorithm Basics

Master fundamental STL algorithms like find, count, copy, and fill that work with any container

Learn how to use STL algorithms to write more expressive, efficient code that works with any container type.

A Simple Example

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> numbers{10, 20, 30, 40, 50, 30, 60};

    // Find first occurrence
    auto it = std::find(numbers.begin(), numbers.end(), 30);
    if (it != numbers.end()) {
        std::cout << "Found 30 at position: "
                  << std::distance(numbers.begin(), it) << "\n";
    }

    // Count occurrences
    int count = std::count(numbers.begin(), numbers.end(), 30);
    std::cout << "Count of 30: " << count << "\n";

    // Count with condition
    int countAbove25 = std::count_if(numbers.begin(), numbers.end(),
                                      [](int n) { return n > 25; });
    std::cout << "Numbers > 25: " << countAbove25 << "\n";

    // Check if all/any/none match condition
    bool allPositive = std::all_of(numbers.begin(), numbers.end(),
                                    [](int n) { return n > 0; });
    std::cout << "All positive: " << std::boolalpha << allPositive << "\n";

    // Fill with value
    std::vector<int> zeros(5);
    std::fill(zeros.begin(), zeros.end(), 7);
    std::cout << "Filled: ";
    for (int n : zeros) std::cout << n << " ";
    std::cout << "\n";

    return 0;
}

Breaking It Down

std::find() - Search for a Value

  • What it does: Searches for the first occurrence of a value in a range
  • Returns: Iterator to the found element, or end() if not found
  • Use for: Checking if an element exists, getting its position
  • Remember: Always check if (it != container.end()) before using the result

std::count() and std::count_if() - Count Elements

  • std::count: Counts how many times a specific value appears
  • std::count_if: Counts elements matching a condition (using a lambda or function)
  • Returns: The count as an integer
  • Remember: count_if is powerful when combined with lambdas for custom conditions

std::all_of(), std::any_of(), std::none_of() - Test Conditions

  • all_of: Returns true if ALL elements match the condition
  • any_of: Returns true if ANY element matches the condition
  • none_of: Returns true if NO elements match the condition
  • Remember: These are more readable than writing loops with flags

std::fill() - Set All Elements to a Value

  • What it does: Sets all elements in a range to the same value
  • Syntax: std::fill(begin, end, value)
  • Use for: Initializing containers, resetting values
  • Remember: There is also fill_n() for filling N elements

Why This Matters

  • STL algorithms represent decades of optimization and testing by experts.
  • Using `std::find()` instead of writing your own loop means less code to write, test, and debug.
  • These algorithms work with any container through iterators, making them incredibly versatile.
  • They are often more efficient than hand-written loops thanks to compiler optimizations and carefully chosen implementations.

Critical Insight

The erase-remove idiom looks weird at first: container.erase(std::remove(...), container.end()). Here is why it works: std::remove() does not actually erase elements because it only has iterators, not the container itself. Instead, it moves all the elements you want to keep to the front and returns an iterator to the new "end". Then erase() actually removes everything from that point to the real end. It is a two-step dance that is more efficient than erasing elements one by one!

Best Practices

Always check iterator validity: After std::find(), check if (it != container.end()) before dereferencing.

Use algorithm + lambda for clarity: std::count_if(v.begin(), v.end(), [](int x) { return x > 0; }) is more readable than a manual loop.

Prefer algorithms over raw loops: They express intent clearly and are often optimized by compilers.

Use the erase-remove idiom correctly: v.erase(std::remove(v.begin(), v.end(), value), v.end()) to actually remove elements.

Common Mistakes

Forgetting to check return value: Dereferencing an end() iterator from std::find() causes undefined behavior. Always check first.

Using std::remove() alone: std::remove() does not actually erase elements - it just moves them. You must call erase() to actually remove them.

Using binary_search on unsorted data: Algorithms like binary_search, lower_bound, and upper_bound require sorted data to work correctly.

Not including <algorithm>: Forgetting to #include <algorithm> will cause compile errors when using these functions.

Debug Challenge

This code tries to find an element but has a bug. Click the highlighted line to fix it:

1 #include <iostream>
2 #include <algorithm>
3 #include <vector>
4
5 int main() {
6 std::vector<int> numbers{10, 20, 30};
7 auto it = std::find(numbers.begin(), numbers.end(), 25);
8 std::cout << "Found: " << *it << "\n";
9 return 0;
10 }

Quick Quiz

  1. What does std::find() return if the element is not found?
The end() iterator
nullptr
-1
  1. Why do you need both std::remove() and erase()?
You do not - remove is enough
remove() moves elements, erase() actually deletes them
It is a legacy design decision
  1. Which algorithm would you use to check if all elements in a vector are positive?
std::any_of
std::count_if
std::all_of

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once