Algorithms
STL algorithms are in the <algorithms>
header. These are generic algorithms which operate on pairs of iterators (a start and end iterator).
C++20 includes versions of these algorithms that operate on ranges and views. This is discussed more in the C++20 section
of the book, but ranges and views are basically like an abstraction over a pair of iterators.
Often times they will take a callable object to perform some computation.
Whenever you write a for loop, the first though should be "is there an algorithm which does this for me?".
Generally, we want to use algorithms as much as possible because they help us avoid making simple indexing or iterator mistakes and are well tested.
As we'll see later, some algorithms may also take an execution policy as their first argument. The execution policy changes their concurrency behavior.
Thus, using STL algorithms, one parameter is all it takes to convert sequential code to concurrent code.
I'll briefly discuss a few common ones, but more information can be found on the documentation of the algorithms library.
A predicate is a function that takes in an element and returns true if that element matches the criteria. A comparator is a function that takes in two arguments and returns true if the first argument should be ordered before the second.
If you are familiar with functional programming, some of the algorithms are very much like common operations in functional
languages such as map
or fold
. I indicate that in the following list with the phrase "analogous to ..." or "similar to ..."
std::for_each(It begin, It end, Func f)
- analogous toiterate
- Calls
f
, passing in the dereferenced iterator for all elements betweenbegin
andend
- Calls
std::for_each_n(It begin, Size num, Func)
- analogous totake
- Same as
for_each
but takes a number indicating the amount of elements to iterate through starting frombegin
.
std::array a = {600, 97, 3784, 474, 3, 906, 0}; std::for_each_n(a.begin(), 3, [](auto e) { std::cout << e << ", "; }); // 600, 97, 3784,
- Same as
std::all_of
/std::any_of
/std::none_of
(It begin, It end, Func pred)
- Iterates through and applies a predicate which takes the dereference of the iterator and returns a bool
std::count(It begin, It end, T elem)
- Iterates through and counts how many elements equal
elem
- Iterates through and counts how many elements equal
std::count_if(begin, end, pred)
- Same as
count
but counts how many times the predicate returns true
- Same as
std::find(begin, end, elem)
- Returns the iterator to the first element that equals
elem
orend
if not found std::find_if
is the same but takes a predicate instead of an element
- Returns the iterator to the first element that equals
std::copy(begin, end, dstBegin)
- Copies elements from range to a new area beginning at
dstBegin
. Does not check whetherdstBegin
is valid or can support the amount of elements in the range std::copy_if(begin, end, dstBegin, pred)
- similar tofilter
- Same as copy but only copies if the predicate returns true for that element
- Copies elements from range to a new area beginning at
std::copy_backward(begin, end, dst)
- Same as copy but stores the elements in
dst
backwards
- Same as copy but stores the elements in
std::move(begin, end, dst)
,std::move_backward(begin, end, dst)
- Self explanatory
std::fill(begin, end elem)
- Assigns
elem
to every iterator in the range std::fill_n(begin, count, elem)
- Assigns
std::generate(begin, end, f)
- Assigns the result of calling
f
to every iterator in the range std::generate_n(begin, count, f)
std::vector<int> nums(100); // 100 integers std::generate(nums.begin(), nums.end(), [count = 0]() mutable { return count++; }); std::vector<char> letters; std::generate_n(std::back_inserter(letters), 100, [](){ return 'A'; }); letters.size(); //100
- Assigns the result of calling
std::transform(begin, end, [optional 2nd range begin], dstBegin, func)
- analogous tomap
, andzip
- Iterates through a range or two, calls the function and stores the result of that function in the destination range
- The second range must be at least as large as the first
- Func takes in the underlying value of the iterator from the first range as the first argument, and if a second range is specified, the value of the second second as the second argument
std::vector a = {100, 200, 300}; std::array b = {100.0, 50.1, -300.67}; std::vector<double> out; std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(out), [](auto a, auto b) { return a + b; }); // out is {200.0, 250.1, -0.67} // transform in-place is allowed std::transform(a.begin(), a.end(), a.begin(), [](auto a) { return a * 2; }); // a is 200, 400, 600
std::swap_ranges(begin, end, otherBegin)
- Swaps the values of each element in two equal sized ranges
std::iter_swap(a, b)
- Swaps the underlying value of two iterators
std::shuffle(begin, end, [optional function which returns a randomly selected iterator difference_type or a UniformRandomBitGenerator])
- Randomly rearranges the elements
std::rotate(begin, new_begin, end)
- Rotates the elements in
begin
toend
to the left so that the iteratornew_begin
becomes the first element of the resulting range
- Rotates the elements in
std::sample(begin, end, out, count, gen)
- Selects
count
elements at random frombegin
andend
and writes them toout
usinggen
(a random number generator) as a source or randomness
- Selects
std::unique(begin, end)
- Removes consecutive duplicates from the range
- Removal is done by moving all elements to be removed to the back of the iterator range, then returning a new ending iterator that does not include these removed elements
std::unique_copy(begin, end, out)
- same but does not perform in-place
std::remove_if(begin, end, predicate)
- Removes all elements satisfying the predicate by moving them to the end of the iterator range
- Returns an iterator to the first removed element which is also an iterator off the back of the range of remaining elements
std::vector v = {100, 200, 300, 400, 500, 600}; const auto new_end = std::remove_if(v.begin(), v.end(), [](auto e) { return e <= 300; }); for(auto num : v) { std::cout << num << std::endl; } // Prints 400, 500, 600, then the remaining numbers (probably: 100, 200, 300) for(auto it = v.begin(); it != new_end; ++it) { std::cout << *it << std::endl; } // Only prints 400, 500, 600
std::partition(begin, end, pred)
- Partitions a range so that elements that satisfy
pred
are at the beginning of the range and those that don't are at the end - Returns an iterator to the point at which the range is partitioned
std::partition_copy
- same idea but not in place
- Partitions a range so that elements that satisfy
std::sort(begin, end, [optional comparator])
std::stable_sort(being, end, [optional comparator])
- sort that guarantees that elements that compare equally are kept in the same order as they werestd::lower_bound(begin, end, elem, [optional comparator])
- Returns an iterator to the first element >= to
elem
orend
of no such element is found - Requires
begin
andend
be sorted or partitioned onval < elem
(ascending order) - Think of finding the lower bound of a series of
elem
within the range - The comparator defines what < means in this context. Will get the first element in which calling the comparator returns
false
. The comparator returns true if the first argument is ordered before the second.
- Returns an iterator to the first element >= to
std::upper_bound(begin, end, elem, [optional comparator])
- Same as lower_bound but finds first element >
elem
orend
if no such element is found - Think of finding the upper bound of a series of
elem
within the range
- Same as lower_bound but finds first element >
std::binary_search(begin, end, val, [optional comparator])
- Returns true if
val
is present, false otherwise
- Returns true if
// Requires begin and end be sorted in ascending order
// gets iterator to first iter that equals elem or end if not found
template<typename Iter, typename T>
auto binarySearch(Iter begin, Iter end, T elem) {
const auto e = std::lower_bound(begin, end, elem);
return *e == elem ? e : end;
}
std::merge(begin, end, begin2, end2, out)
- Merges two sorted ranges and puts the result starting from the
out
iterator
- Merges two sorted ranges and puts the result starting from the
std::make_heap(begin, end, [optional comparator])
- Makes the range a max heap
std::push_heap(begin, end, elem, [optional comparator])
- Appends an element to an existing heap
std::pop_heap(begin, end, [comparator])
set_union
,set_intersection
,set_difference
- Treats two ranges as sets and performs the set operation
max_element
,min_element
,minmax_element
- gets the max, min, and min and max, respectively from a range
lexicographic_compare(begin, end, begin2, end2, [comparator])
- returns true if the first range is lexicographically less than the second
The following are part of the <numeric>
header
-
std::iota(begin, end, start)
- Fills the range with sequentially increasing values starting from
start
- Fills the range with sequentially increasing values starting from
-
std::accumulate(begin, end, start, [optional operation])
- similar tofold_left
orreduce
- Sums all the elements in the range with an initial value of
start
- If a binary function is passed, calls that function with the first argument as the current accumulation value (initialized to
start
) and the second argument as the current value in the range. The returned value becomes the new accumulated value std::reduce
is similar toaccumulate
but it can take an execution policy, and the initial value is optional
const std::vector v = {10.0, 5.4, 2, -1.2}; const auto pi_v = std::accumulate(v.begin(), v.end(), 1, [](auto a, auto b) { return a * b; }); // 1 (initial value) * 10 * 5.4 * 2 * -1.2
- Sums all the elements in the range with an initial value of
-
std::partial_sum(begin, end, out, [optional op])
- Same as accumulate except saves each accumulation step as a separate element in
out
- Same as accumulate except saves each accumulation step as a separate element in