Iterators and algorithms

Intermediate

Introduction

Loops that search, count and sort are written again and again, and each is a chance to make a small mistake. The standard library provides well-tested versions that say exactly what they are doing. A line that reads sort(...) needs no explanation.

How it works

An iterator is like a pointer into a container. *it reads the element, and ++it moves on. v.begin() points at the first element, and v.end() points just past the last, which is why it must never be read. Together they describe a range.

Algorithms come from <algorithm> and take such a range:

AlgorithmDoes
sort(b, e)sorts, smallest first
sort(b, e, cmp)sorts by your compare function
reverse(b, e)reverses the order
find(b, e, x)returns an iterator to x, or e
count(b, e, x)counts the matches
find_if, count_ifthe same, using a test function
min_element, max_elementreturns an iterator to the smallest or largest
transform(b, e, out, f)applies f to each element
unique(b, e)removes neighbouring repeats
lower_bound, upper_bound, binary_searchfast search of sorted data

Passing a function. Without brackets, a function's name is a value, so it can be handed to an algorithm. A function that returns true or false is called a predicate:

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

bool is_negative(int x) {
    return x < 0;
}

int main() {
    vector<int> v = {4, -2, 7, -9};
    auto it = find(v.begin(), v.end(), 7);
    if (it != v.end()) {
        cout << "found at " << it - v.begin() << endl;
    }
    cout << count_if(v.begin(), v.end(), is_negative) << endl;
    cout << *find_if(v.begin(), v.end(), is_negative) << endl;
    return 0;
}
Open in compiler

Removing repeats. unique needs sorted data, and it only moves the unique items to the front. The usual form erases the leftovers: v.erase(unique(v.begin(), v.end()), v.end());.

Syntax

sort(v.begin(), v.end());
auto it = find(v.begin(), v.end(), x);
if (it != v.end()) { ... }
int n = count_if(v.begin(), v.end(), is_even);

Example

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

bool larger_first(int a, int b) {
    return a > b;
}

int main() {
    vector<int> v = {5, 1, 4};
    sort(v.begin(), v.end());
    cout << v[0] << " " << v[2] << endl;
    sort(v.begin(), v.end(), larger_first);
    cout << v[0] << " " << v[2] << endl;
    cout << *max_element(v.begin(), v.end()) << endl;
    return 0;
}
Open in compiler

Line by line

  1. sort with two iterators puts the numbers in increasing order.
  2. v[0] and v[2] are now the smallest and the largest.
  3. The second sort is given a compare function, which makes it sort from largest to smallest.
  4. max_element returns an iterator, and * reads the value.

Expected result

1 5
5 1
5

In the real world

The standard algorithms are heavily optimised and thoroughly tested, so using them means faster and safer code. Experienced C++ programmers tend to reach for an algorithm first, and write a loop only if nothing fits.

Common mistakes

  • Reading *v.end(), which is past the last element.
  • Not checking find against end().
  • Using binary_search on unsorted data.
  • Forgetting that unique needs sorted data, and the erase afterwards.

Best practices

  • Check for a standard algorithm before you write a loop.
  • Compare a compare function strictly: use < or >, never <=.
  • Include <algorithm>, and do not rely on another header to bring it in.

Summary

Iterators point into containers, and begin and end describe a range. Algorithms take a range and do common jobs reliably. Functions can be passed to them to customise their behaviour.

Quick check

1. What does v.end() point at?
2. How do you tell that find failed?
3. What must be true before calling binary_search?
4. What is a predicate?

Key takeaways

  • end() is one past the last element, so never read it.
  • Algorithms work on a pair of iterators.
  • Prefer a standard algorithm to a hand-written loop.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.