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
204: The <algorithm> Header: More Algorithms
Think of the <algorithm> header like a high-end professional kitchen. You've already learned how to use the basic tools—the knives (sorting) and the scales (counting). But if you look deeper into the drawer, there are specialized tools designed for very specific tasks. Instead of spending ten minutes manually chopping vegetables one by one, you use a food processor. Instead of manually sorting through a bin of produce to find the bruised apples, you use a sorting tray that separates them into two distinct piles in one pass.
In C++, these "specialized tools" are algorithms that handle common data manipulation patterns. I've seen too many developers write 15-line for loops to do things that the Standard Template Library (STL) can do in a single, highly optimized line of code. Let's look at a few of the ones I actually use in production.
Prepping Your Data with std::transform
I like to think of std::transform as a mapping tool. It takes a range of data, applies a specific operation to every single element, and shoves the result into a destination. It's perfect for when you have a list of "raw" values and you need a list of "processed" values.
For example, imagine you have a list of product prices in cents, but you need to display them as formatted strings with a currency symbol. Instead of writing a loop and manually pushing back into a new vector, you do this:
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
int main() {
std::vector<int> prices_cents = {1999, 550, 1200, 4599};
std::vector<std::string> display_prices(prices_cents.size());
std::transform(prices_cents.begin(), prices_cents.end(), display_prices.begin(),
[](int cents) {
return "$" + std::to_string(cents / 100.0);
});
// display_prices now contains {"$19.99", "$5.50", "$12.00", "$45.99"}
}
One thing to watch out for: make sure your destination container (in this case, display_prices) already has enough space allocated. If it's empty, you'll be writing into memory you don't own, and your program will crash. I usually initialize the vector with the correct size immediately, as I did above.
Dividing the Room with std::partition
Sometimes you don't need to fully sort a list—sorting is expensive. Often, you just need to split your data into two groups: those that meet a certain criteria and those that don't. This is exactly what std::partition does. It doesn't guarantee the order within the two groups, but it guarantees that all "true" elements come before all "false" elements.
Imagine you're managing a list of Game Entities, and you need to separate the ones that are "active" from the ones that are "inactive" so you can run physics updates only on the active ones.
#include <algorithm>
#include <vector>
struct Entity {
int id;
bool active;
};
int main() {
std::vector<Entity> entities = {{1, true}, {2, false}, {3, true}, {4, false}, {5, true}};
auto pivot = std::partition(entities.begin(), entities.end(), [](const Entity& e) {
return e.active;
});
// All entities from entities.begin() to pivot are active.
// All entities from pivot to entities.end() are inactive.
}
The pivot iterator returned by the function is the magic part. It marks the exact boundary where the "true" group ends and the "false" group begins. It's incredibly efficient because it swaps elements in place.
Shifting Perspective with std::rotate
Finally, let's talk about std::rotate. This is a bit of a brain-bender the first time you see it. It doesn't "rotate" the data in a circle like a wheel; rather, it takes a specific element and makes it the new first element, shifting everything else accordingly.
I use this all the time for implementing circular buffers or turn-based systems. If you have a list of players and the current player's turn is over, you "rotate" the list to bring the next player to the front.
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
int main() {
std::vector<std::string> players = {"Alice", "Bob", "Charlie", "Dave"};
// We want Bob to be at the front.
// We tell rotate: "Make the element at index 1 the new beginning."
std::rotate(players.begin(), players.begin() + 1, players.end());
// Players is now: {"Bob", "Charlie", "Dave", "Alice"}
}
The second argument is the "new first" element. Everything before that point gets moved to the end of the range. It's a clean, efficient way to handle queues without actually using a std::deque if you don't need the specific performance characteristics of one.
📋 Practical Task
The Turn-Based Combat Queue Manager
You are building a combat system for a tactical RPG. You have a list of combatants, each with a name and an initiative score. You need to perform the following operations using the <algorithm> header:
- Filter the Fallen: Use
std::partitionto move all combatants withhp > 0to the front of the vector and those withhp <= 0to the back. - Apply Buffs: Use
std::transformto increase the attack power of all remaining living combatants by 10%. - Cycle Turns: Use
std::rotateto move the current fighter at the front of the "living" group to the end of the "living" group, effectively passing the turn to the next person in line.
Create a struct Combatant with std::string name, int hp, and float attackPower. Initialize a vector with at least 5 combatants (some dead, some alive) and implement these three steps in order. Print the list after each step to verify the movement and transformations.
There are no comments for now.