Skip to main content
    Courses/C++/STL Algorithm Mastery

    Lesson 18 • Advanced

    STL Algorithm Mastery

    By the end of this lesson you'll replace hand-written loops with the C++ Standard Library's <algorithm> and <numeric> tools — sorting with your own rules, searching, summing, transforming, and safely deleting elements — using one line where you used to write fifteen.

    What You'll Learn

    • Sort a range with std::sort and a custom comparator lambda
    • Search with std::find, count with std::count and std::count_if
    • Fold a range to one value with std::accumulate (from <numeric>)
    • Reshape data with std::transform and act with std::for_each
    • Find extremes with std::max_element / std::min_element
    • Delete elements correctly with the erase-remove idiom

    💡 Real-World Analogy

    Think of STL algorithms as power tools. You could cut every plank with a hand saw (your own for loop), but a builder reaches for the right power tool: a circular saw to cut (sort), a stud finder to locate (find), a tape measure to total up (accumulate). Each tool clamps onto a stretch of work — that's the iterator range, from begin() to end(). You supply the rule (a lambda: "cut here", "keep this one") and the tool does the repetitive work, faster and with fewer mistakes than doing it by hand.

    1. Sort, Find & Count

    std::sort rearranges a range in place — ascending by default. To sort by your rule, pass a third argument: a comparator. A comparator is a lambda that takes two elements and returns a bool answering one question — "should a come before b?" Return a > b and you've flipped it to descending. find hunts for an exact value and hands back an iterator (compare it to end() to check for a miss), while count and count_if tally matches. Read this worked example, run it, then you'll write your own.

    Worked example: sort, comparator, find, count

    Read every comment, run it, and check the output matches.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>   // sort, find, count, count_if, min/max_element
    using namespace std;
    
    int main() {
        vector<int> nums = {42, 17, 8, 95, 23, 61, 3};
    
        // sort — rearranges the range in place, ascending by default.
        // You pass a RANGE: begin() ... end() (end is one-past-the-last).
        sort(nums.begin(), nums.end());
        cout << "Ascending:  ";
        for (int n : nums) cout << n << " ";   // 3 8 17 23 42 61 95
        cout << endl;
    
        // Custom comp
    ...

    Your turn. The program below is almost complete — fill in the two blanks marked ___ using the hints, then run it and check it against the expected output.

    🎯 Your turn: descending sort & count_if

    Fill in the comparator and the predicate, then verify your output.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int main() {
        // 🎯 YOUR TURN — replace each ___ then press "Try it Yourself".
        vector<int> scores = {72, 48, 91, 63, 30, 88, 55};
    
        // 1) Sort scores HIGHEST first using a comparator lambda.
        //    A comparator returns "should a come before b?".
        sort(scores.begin(), scores.end(), [](int a, int b) {
            return ___;          // 👉 a > b   (so bigger values come first)
        });
    
        cout << "Ranked:
    ...

    2. Accumulate, Transform & for_each

    std::accumulate (from <numeric>, not <algorithm>) folds a whole range into a single value — its third argument is both the starting total and the type of the result. std::transform applies a function to every element and writes the results into another range. std::for_each runs an action on each element without producing a new range. And min_element / max_element return an iterator to the smallest or largest value — remember to dereference it with * to read the value out.

    Worked example: accumulate, transform, min/max, for_each

    See how a range folds to one value, and how transform reshapes data.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>   // transform, for_each, min_element, max_element
    #include <numeric>     // accumulate  (lives here, NOT in <algorithm>)
    using namespace std;
    
    int main() {
        vector<int> prices = {1299, 2499, 599, 3999, 899};   // cents
    
        // accumulate — fold a whole range into ONE value.
        // The 3rd argument (0) is the starting total AND fixes the type.
        int totalCents = accumulate(prices.begin(), prices.end(), 0);
        cout << "Total cents: 
    ...

    Now you try. Total a list of workout minutes, then convert each one to hours. Fill in the two blanks — watch the type on the accumulate seed and the division:

    🎯 Your turn: total & convert with accumulate + transform

    Seed accumulate correctly and divide by 60.0 to keep decimals.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <numeric>
    using namespace std;
    
    int main() {
        // 🎯 YOUR TURN — fill in the blanks, then run.
        vector<int> minutes = {30, 45, 15, 60, 20};   // workout lengths
    
        // 1) Add them all up with accumulate. Start the total at 0.
        int totalMins = accumulate(minutes.begin(), minutes.end(), ___);  // 👉 0
        cout << "Total minutes: " << totalMins << endl;
    
        // 2) Convert each value to HOURS (a double) using transform.
       
    ...

    3. Deleting Elements: the Erase-Remove Idiom

    This is the one that surprises everybody. std::remove and std::remove_if do not actually delete anything. An algorithm only sees a pair of iterators — it has no power to resize the container. So remove just shuffles the elements you're keeping to the front and returns an iterator to the new logical end; the leftover slots are still there. To truly shrink the container you pass that iterator to the container's own erase() method. Doing both together is the famous erase-remove idiom.

    Worked example: remove vs erase, and remove_if

    Watch the size NOT change after remove, then change after erase.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>   // remove, remove_if
    using namespace std;
    
    void printVec(const string& label, const vector<int>& v) {
        cout << label << ": ";
        for (int n : v) cout << n << " ";
        cout << endl;
    }
    
    int main() {
        // remove() does NOT delete anything. It shifts the elements you keep
        // to the front and returns an iterator to the new "logical end".
        // Everything after that point is leftover junk — still in the vector!
        vector<int> dat
    ...

    Pro Tips

    • 💡 A comparator must be a strict weak ordering: use < not <=. Returning true for equal elements breaks sort and can crash your program.
    • 💡 min/max_element return iterators, not values. Dereference with *: int hi = *max_element(v.begin(), v.end());.
    • 💡 Seed accumulate with the right type: accumulate(v.begin(), v.end(), 0.0) sums as double; 0 would truncate to int.
    • 💡 Always pair remove_if with erase — by itself it leaves stale elements behind the new end.

    Common Errors (and the fix)

    • "My remove didn't remove anything!": remove only shifts elements and returns the new end — it can't resize the container. You must follow it with v.erase(newEnd, v.end()). That's the erase-remove idiom.
    • Crash / "invalid comparator": your comparator returned true for equal elements (you wrote <= instead of <). A comparator must be a strict weak ordering — strictly <, never <=.
    • "'accumulate' was not declared in this scope": you forgot #include <numeric>. accumulate lives there, not in <algorithm>.
    • Garbage value from max_element: you forgot the *. The function returns an iterator; read the value with *max_element(...).
    • Off-by-one / mismatched ranges: end() points one past the last element, so a range is [begin, end). Passing v.end() as a start, or mixing iterators from two different containers, is undefined behaviour.

    📋 Quick Reference

    TaskCodeReturns
    Sort ascendingsort(v.begin(), v.end())void (in place)
    Sort by rulesort(b, e, [](int a, int b){ return a>b; })void (in place)
    Find a valuefind(v.begin(), v.end(), 42)iterator / end()
    Count matchescount_if(b, e, pred)how many
    Sum a rangeaccumulate(b, e, 0)the total
    Map each elementtransform(b, e, out, fn)writes to out
    Largest value*max_element(b, e)the value
    Delete matchesv.erase(remove_if(b, e, pred), e)shrinks v

    Frequently Asked Questions

    Q: Do I always need to include <algorithm> and <numeric>?

    Yes. sort, find, count_if, transform, for_each, remove_if, min_element and max_element all live in <algorithm>. accumulate lives in <numeric>. If you forget the include you'll get an 'X was not declared in this scope' error.

    Q: Why does erasing after remove_if look so awkward?

    remove_if only shuffles the kept elements to the front and returns an iterator to the new logical end — it cannot resize the container because algorithms only see iterators, not the container itself. You pass that returned iterator to the container's own erase() to actually shrink it. This two-step combo is the 'erase-remove idiom'.

    Q: What does a comparator lambda return?

    A bool. It answers one question: 'should a come before b?' Return true when a should sort earlier. For ascending order return a < b; for descending return a > b. It must NOT return true for equal values, or you break sort's rules.

    Q: What's the difference between find and find_if?

    find looks for a specific value (find(v.begin(), v.end(), 42)). find_if looks for the first element that makes a predicate return true (find_if(v.begin(), v.end(), [](int n){ return n > 50; })). Both return end() when nothing matches.

    Q: Why does accumulate start with a value like 0?

    That third argument is the starting total (the 'seed') and it also fixes the result type. accumulate(v.begin(), v.end(), 0) sums into an int; accumulate(v.begin(), v.end(), 0.0) sums into a double. Seeding with the wrong type silently truncates your decimals.

    Mini-Challenge: Temperature Report

    No blanks this time — just a brief and an outline to keep you on track. Combine sort, min_element/max_element, accumulate, and count_if into one small report. Build it, run it, and check your output against the example in the comments.

    🎯 Mini-Challenge: build a temperature report

    Sort, find the extremes, average with accumulate, and count warm days yourself.

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <numeric>
    using namespace std;
    
    int main() {
        // 🎯 MINI-CHALLENGE: Temperature report
        vector<int> temps = {18, 25, 31, 14, 22, 29, 16};
    
        // 1. Sort temps from COLDEST to hottest (a comparator lambda, or plain sort).
        // 2. Use min_element / max_element to print the low and the high.
        //    Remember they return iterators — dereference with * to read the value.
        // 3. Use accumulate to get the total, then divi
    ...

    🎉 Lesson Complete

    • sort orders a range in place; a comparator lambda sets your own rule (strict <, never <=)
    • find returns an iterator; count / count_if tally matches
    • accumulate (from <numeric>) folds a range to one value — seed it with the right type
    • transform reshapes data into another range; for_each acts on each element
    • min_element / max_element return iterators — dereference with *
    • remove_if doesn't shrink anything — pair it with erase() (the erase-remove idiom)
    • Next lesson: Advanced Containers — the internals of vector, list, map, unordered_map, and deque

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service