C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
70: Algorithms Library: sort, find, transform
I've spent far too many years of my career writing manual for loops to move data around or find a specific element in a list. It's a habit that's hard to break, but once you lean into the <algorithm> library, you realize that not only is your code shorter, but it's usually faster and much harder to mess up. Today, we're going to look at three heavy hitters: sort, find, and transform.
To keep this concrete, let's imagine we're building a simple leaderboard for a game. We have a list of player scores, and we need to do a few things: check if anyone hit a specific milestone, rank them from highest to lowest, and then apply a "seasonal bonus" to everyone's score.
Organizing the Scoreboard
First, we'll start with a simple std::vector of integers. I'm using a vector because these algorithms work on iterators, and vectors provide the contiguous memory that makes these operations efficient.
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // for std::greater
int main() {
std::vector<int> scores = {450, 1200, 800, 1500, 300, 1200};
// We'll add logic here...
}
Hunting for a Specific Score
Let's say we want to know if any player hit exactly 1500 points to trigger a special achievement. Instead of looping through the vector with an index, we use std::find. It takes a range (begin and end) and the value we're looking for.
auto it = std::find(scores.begin(), scores.end(), 1500);
if (it != scores.end()) {
std::cout < "Found a master player!" < std::endl;
} else {
std::cout < "No one hit 1500 yet." < std::endl;
}
One thing to remember: std::find doesn't return an index; it returns an iterator. If it doesn't find the value, it returns the end() iterator. Always check against end() before dereferencing it, or you're asking for a crash.
Sorting the Leaders (and fixing my mistake)
Now, we want to display these scores from highest to lowest. I'll start with the basic std::sort.
std::sort(scores.begin(), scores.end());
Wait. I just realized I did exactly what I always do when I'm rushing: std::sort defaults to ascending order. My leaderboard now has the worst players at the top. That's not what we want. To fix this, I can provide a third argument—a comparison function. C++ provides std::greater<int>() in the <functional> header specifically for this purpose.
// Correcting the sort to be descending
std::sort(scores.begin(), scores.end(), std::greater<int>{});
Now the 1500 is at the front, and the 300 is at the back. Much better.
Applying a Global Bonus
Finally, let's say the game developers decided to give everyone a 10% bonus for the holidays. We could loop through and multiply each element, but std::transform is the "correct" tool here. It takes an input range, an output destination, and an operation to apply.
I'll use a lambda function here to handle the multiplication. Since I want to modify the existing vector, I'll set the output destination to scores.begin().
std::transform(scores.begin(), scores.end(), scores.begin(), [](int s) {
return static_cast<int>(s * 1.1);
});
I used static_cast<int> because our vector stores integers, and multiplying by 1.1 produces a double. If I didn't cast it, the compiler might give me a warning about implicit conversion. It's a small detail, but it keeps the build logs clean.
If we print the results now, we'll see our scores sorted descending and boosted by 10%.
📋 Practical Task
Exercise: The Clearance Sale Price Adjuster
You are tasked with managing a list of product prices for a store's clearance sale. Create a program that does the following:
- Initialize a
std::vector<double>with the following prices:{19.99, 5.50, 42.00, 12.75, 5.50, 100.00}. - Use
std::findto check if there is a product priced exactly at42.00. Print "Product found" or "Product not found". - Use
std::sortto organize the prices in ascending order (cheapest first). - Use
std::transformto apply a 20% discount to every price in the list (multiply by 0.8). - Print the final list of discounted prices to the console.
There are no comments for now.