Ready-made helpers for common loops
Finding the smallest value, the largest value and the total of a vector are jobs a loop can do, and they come up constantly. The C++ library has them ready-made:
min_element(v.begin(), v.end())finds the smallest value of the vectorv.max_element(v.begin(), v.end())finds the largest value.accumulate(v.begin(), v.end(), 0)adds all the values to a start value, here 0.
begin() and end() mark the whole vector. min_element and max_element live in <algorithm> and accumulate in <numeric>. The lesson already includes both headers.
vector<int> prices = {4, 9, 2, 7};
cout << *min_element(prices.begin(), prices.end()) << endl; // 2
cout << *max_element(prices.begin(), prices.end()) << endl; // 9
cout << accumulate(prices.begin(), prices.end(), 0) << endl; // 22
Why the star?
min_element doesn't return the smallest value. It returns an iterator, which marks where that value sits in the vector. The * in front reads the value at that spot. Without it the code doesn't compile where an int is expected. Because the result is a position, you can also turn it into an index by subtracting begin(): for the minutes in this lesson, min_element(...) - minutes.begin() is 2, the index of the 15.
The start value sets the type
accumulate adds into a variable that has the type of the start value. With 0 that is an int. For a vector of doubles this loses the fractions: with {1.5, 2.5, 3.5}, a start of 0 gives 6, while a start of 0.0 gives 7.5. Use 0.0 for doubles.
Watch out for an empty vector
On an empty vector, min_element returns v.end(), a spot past the last value, where there is nothing to read. Reading it with * is undefined behavior: the program may crash or print garbage. Check v.empty() first. accumulate on an empty vector is safe: it returns the start value, 0.
Built-ins against loops
You can always write the loop yourself, and a loop is the right tool when the rule is unusual. The built-ins are shorter, say what they do in their name, and have no starting-value mistake to make: a hand-written minimum that starts at 0 gives a wrong answer for a vector of positive numbers. For a plain smallest, largest or total, prefer them.
The built-ins work on a range, so they can also cover part of a vector. For example, accumulate(v.begin(), v.begin() + 3, 0) adds only the first three values.
The exercise, step by step
minutes holds the minutes practiced on each day of a week. The task stores the shortest day, the longest day and the week's total.
- Shortest:
int shortest = *min_element(minutes.begin(), minutes.end()); - Longest: the same with
max_element. - Total:
accumulateover the whole vector with a start value of 0, which also makes the total anint.
vector<int> minutes = {25, 40, 15, 60, 30, 45, 20};
int shortest = *min_element(minutes.begin(), minutes.end());
int longest = *max_element(minutes.begin(), minutes.end());
int total = accumulate(minutes.begin(), minutes.end(), 0);
cout << "shortest = " << shortest << endl;
cout << "longest = " << longest << endl;
cout << "total = " << total << endl;
The output:
shortest = 15
longest = 60
total = 235
Both min_element and max_element return the first matching position when a value appears more than once, which only matters if you use the position and not just the value.
Key takeaways
*min_element(v.begin(), v.end())and*max_element(...)give the smallest and largest values.accumulate(v.begin(), v.end(), 0)sums a vector; the start value decides the type, so use0.0for doubles.- Never dereference the result of
min_elementormax_elementon an empty vector.
