Exercises — STL algorithms — sort, find, transform, accumulate, copy, all_of, any_of

Figure: a source range on the left feeds through an algorithm into a sink. v.begin() overwrites fixed slots (needs room); back_inserter(w) appends new slots (grows). This is why an empty destination needs the funnel.
Level 1 — Recognition
You are handed a sentence in plain English. Your only job: name the one STL algorithm that expresses it, and say what it returns.
Exercise 1.1 (L1)
"Put the numbers in a vector<int> into increasing order." Which algorithm, and what does it return?
Recall Solution 1.1
Algorithm: ==sort== — specifically sort(v.begin(), v.end()).
Returns: nothing (void). It rearranges the elements in place; the container itself is the result.
Why not the others? find locates one element, transform maps a function, copy duplicates — none of them reorder. Only sort reorders a range.
Exercise 1.2 (L1)
"Tell me whether every student's score is at least 40." Which algorithm?
Recall Solution 1.2
Algorithm: ==all_of== with predicate [](int s){ return s >= 40; }.
Returns: a bool. true only if every element passes; on an empty vector it returns true (vacuous truth — no failing student exists).
any_of would answer "is there at least one passer", which is a different question.
Level 2 — Application
Now write / trace the actual call and give the exact result.
Exercise 2.1 (L2)
Given vector<int> v{5, 2, 8, 1, 9, 3};, sort it descending with a lambda. Write the call and give the final vector.
Recall Solution 2.1
#include <vector>
#include <algorithm>
using namespace std;
// ...
sort(v.begin(), v.end(), [](int a, int b){ return a > b; });
// v = {9, 8, 5, 3, 2, 1}Why a > b? The comparator cmp(a,b) must return true when a should come before b. "Bigger comes first" is exactly a > b, giving descending order. The default operator< gives ascending. See the comparator figure below.

Figure: the comparator is a yes/no gate. cmp(a,b) = "should a sit before b?" With a < b (teal) small values win the front → ascending. With a > b (orange) big values win the front → descending.
Exercise 2.2 (L2)
Given vector<double> v{1.5, 2.5, 3.0};, compute the sum correctly. What goes wrong if you write accumulate(v.begin(), v.end(), 0)?
Recall Solution 2.2
#include <vector>
#include <numeric>
using namespace std;
// ...
double s = accumulate(v.begin(), v.end(), 0.0); // s == 7.0The bug with 0: the accumulator's type equals the type of init. With int init = 0, every partial sum is stored in an int:
(truncated), , . You'd get 6, not 7.0.
Fix: pass 0.0 so the accumulator is double. The correct total is .
Exercise 2.3 (L2)
Given vector<int> v{1, 2, 3, 4, 5};, produce a new vector w holding each element cubed, without changing v. Write the call.
Recall Solution 2.3
#include <vector>
#include <algorithm>
#include <iterator> // <-- needed for back_inserter
using namespace std;
// ...
vector<int> w; // starts empty (size 0)
transform(v.begin(), v.end(),
back_inserter(w), // grows w via push_back (see funnel figure)
[](int x){ return x*x*x; });
// w = {1, 8, 27, 64, 125}Why back_inserter(w)? transform assigns into dest; it does not grow the container. Since w is empty, w.begin() points nowhere valid. A back_inserter — defined in <iterator> — turns each write into a push_back, growing w safely. Cubes: .
Level 3 — Analysis
Predict outputs and diagnose subtle behaviour.
Exercise 3.1 (L3)
What does any_of return on an empty vector, and what does all_of return? Explain using the fold identities.
Recall Solution 3.1
On an empty range:
all_of→ ==true==. It AND-folds starting fromtrue: . No element can be a counterexample, so "all pass" holds vacuously.any_of→ ==false==. It OR-folds starting fromfalse: . There is no element to make it true. Mental model:all_ofis with identitytrue;any_ofis with identityfalse. The start value is the empty-range answer — see the fold figure.

Figure: folding as a running box. all_of starts at true and AND-folds each result in; any_of starts at false and OR-folds. With no elements the box keeps its start value — that start value is exactly the empty-range answer.
Exercise 3.2 (L3)
Trace this. What is idx?
#include <vector>
#include <algorithm>
using namespace std;
// ...
vector<int> v{10, 20, 30, 40};
auto it = find(v.begin(), v.end(), 25);
size_t idx = it - v.begin();Recall Solution 3.2
25 is not in v, so find returns v.end() — one past the last element. Then
So idx == 4. The danger: the code did not check it != v.end() first. It silently produced 4, an index that looks like a real position but is actually the "not found" sentinel turned into a number. Always test if (it != v.end()) before dereferencing or measuring position.
Exercise 3.3 (L3)
accumulate(v.begin(), v.end(), 10, [](int a, int b){ return a - b; }) with v{1, 2, 3}. Compute the result and explain why left-vs-right fold matters here.
Recall Solution 3.3
accumulate is a left fold: it applies op(acc, e) walking left to right, starting from init.
Result = 4.
Why the direction matters: subtraction is not associative. A right fold would give a different number (). Because accumulate fixes the direction (left), op is evaluated as ((10-1)-2)-3. Never assume commutativity/associativity when you pass a non-+ op.
Level 4 — Synthesis
Combine two or more algorithms to solve one task.
Exercise 4.1 (L4)
From vector<int> v{4, 7, 2, 9, 5, 1}, compute the sum of the elements greater than 4, using transform + accumulate (no raw loop). Give code and the number.
Recall Solution 4.1
Idea: map each element to itself-if->4-else-0, then sum.
#include <vector>
#include <algorithm> // transform
#include <numeric> // accumulate
#include <iterator> // back_inserter
using namespace std;
// ...
vector<int> masked;
transform(v.begin(), v.end(), back_inserter(masked),
[](int x){ return x > 4 ? x : 0; });
// masked = {0, 7, 0, 9, 5, 0}
int total = accumulate(masked.begin(), masked.end(), 0); // 21Kept: . Sum .
Why this pipeline? transform zeroes out the elements we don't want (0 is the identity for +, so they vanish in the sum), and accumulate folds them. Alternatively accumulate with a custom op a + (b>4 ? b : 0) does it in one pass — same answer, 21.
Exercise 4.2 (L4)
Given vector<int> v{3, 8, 3, 5, 8, 1}, produce a sorted copy with duplicates kept, leaving v unchanged. Then answer: is the sorted copy in non-decreasing order? Give the sorted copy.
Recall Solution 4.2
#include <vector>
#include <algorithm> // copy, sort
#include <iterator> // back_inserter
using namespace std;
// ...
vector<int> s;
copy(v.begin(), v.end(), back_inserter(s)); // s is a copy of v (grows via push_back)
sort(s.begin(), s.end()); // sort the copy only
// s = {1, 3, 3, 5, 8, 8}, v unchangedSorted copy: {1, 3, 3, 5, 8, 8}.
The "is it sorted?" check is true — after sort it is by construction non-decreasing. v still equals {3,8,3,5,8,1}.
Why copy first? sort mutates in place. To preserve the original you must sort a duplicate; copy with a back_inserter builds that duplicate safely into an empty vector.
Level 5 — Mastery
Design a small solution and reason about correctness across all cases and cost.
Exercise 5.1 (L5)
Write a function bool isPalindromeDigits(int n) that returns true iff the decimal digits of a non-negative n read the same forwards and backwards. Build the forward digit string and a reversed copy, then compare the two sequences with std::equal (and cross-check with an all_of-based version). Handle n = 0. State the time complexity. Test on n = 12321 and n = 12345.
Recall Solution 5.1
#include <string>
#include <algorithm> // copy, equal, all_of
#include <iterator> // back_inserter
using namespace std;
bool isPalindromeDigits(int n) {
string s = to_string(n); // "0" for n==0, never empty
string r; // reversed copy, built via back_inserter
copy(s.rbegin(), s.rend(), // rbegin/rend walk s backwards
back_inserter(r)); // append each char -> r is s reversed
// Compare the forward and reversed digit sequences element-by-element:
return equal(s.begin(), s.end(), r.begin()); // true iff every position matches
}std::equal(first1, last1, first2) walks both ranges in lockstep and returns true iff every pair of corresponding elements is equal — exactly the "read the same forwards and backwards" test. Because r is the reverse of s, equal succeeds precisely when s is a palindrome.
all_of-based cross-check (same idea by index): position i must match position d-1-i.
#include <numeric> // iota
#include <vector>
bool isPalDigits2(int n) {
string s = to_string(n);
size_t d = s.size();
vector<size_t> idx(d);
iota(idx.begin(), idx.end(), 0); // idx = {0,1,...,d-1}
return all_of(idx.begin(), idx.end(),
[&](size_t i){ return s[i] == s[d-1-i]; });
}all_of AND-folds "does character i mirror character d-1-i?" over all positions — true only if every mirror-pair matches. On an empty index list it is vacuously true, but to_string never yields an empty string so we never hit that.
Test values:
n = 0→s = "0",r = "0"→equalcompares one char, matches →true.n = 12321→s = "12321",r = "12321"→true.n = 12345→s = "12345",r = "54321"→ first pair'1'vs'5'differs →false. Cost: let = number of digits ().to_stringis , the reversedcopyis ,equalis . Total — see Time complexity Big-O. All cases covered: single digit (always true), even/odd digit counts (equalhandles both), leading-zero worry (there is none — integers have no leading zeros).
Exercise 5.2 (L5)
You must confirm a vector<int> v is a valid non-decreasing sorted sequence without re-sorting (that would hide bugs). Design an check built on std::all_of over adjacent index positions. Apply it to {1, 3, 3, 5} and to {1, 3, 2, 5}.
Recall Solution 5.2
all_of sees one element at a time, so we hand it the index of each left-hand neighbour and let the predicate look at both v[i] and v[i+1]. The predicate must fail only on a strict decrease:
#include <vector>
#include <algorithm> // all_of
#include <numeric> // iota
using namespace std;
bool sortedNondec(const vector<int>& v){
if (v.size() < 2) return true; // empty / single -> vacuously sorted
vector<size_t> idx(v.size() - 1); // one slot per adjacent pair
iota(idx.begin(), idx.end(), 0); // idx = {0,1,...,n-2}
return all_of(idx.begin(), idx.end(),
[&](size_t i){ return v[i] <= v[i+1]; }); // no descent
}all_of AND-folds "is each pair non-decreasing (v[i] <= v[i+1])?" — it short-circuits at the first descent. This is exactly what the library's is_sorted computes internally; here we build it so the mechanism is visible.
{1, 3, 3, 5}: pairs(1,3),(3,3),(3,5)all satisfy<=(note3 <= 3is true, equal neighbours allowed) →true.{1, 3, 2, 5}: pair(3,2)fails3 <= 2→all_ofreturnsfalse. Cost: one pass, predicate calls ⇒ , far cheaper than the of re-sorting — and it detects disorder instead of erasing it. Edge cases: empty vector and single element returntrue(guarded byv.size() < 2, matching vacuous "sorted"). Using<=(not<) in the predicate is what allows equal adjacent values in a non-decreasing sequence.
Recall One-line self-test recap
Empty range: all_of ::: true
Empty range: any_of ::: false
find miss on a 4-element vector, then it - begin() ::: 4 (the size / end() position)
accumulate(v, 0.0) vs accumulate(v, 0) on doubles ::: 0.0 keeps precision; 0 truncates each partial sum to int
What grows an empty destination during copy/transform? ::: back_inserter(dst) (from <iterator>) — it push_backs each write
Compare forward vs reversed digit sequence ::: std::equal(s.begin(), s.end(), r.begin())
Preserve original before sorting ::: copy (or copy-construct) then sort the copy