C++ Algorithms: sort, find, transform

In modern C++ programming, the algorithms provided by the Standard Template Library (STL) are key to improving code efficiency and reducing repetitive logic. The three fundamental algorithms—std::sort, std::find, and std::transform—form the cornerstone of data processing. Mastering them not only simplifies daily development tasks but also helps developers write more robust and maintainable code. This article provides an in-depth analysis of the principles, usage, and practical application scenarios of these three algorithms.
Sorting is one of the most common operations in data processing. The std::sort function in the C++ STL was introduced in the C++98 standard, and its core characteristics lie in its efficiency and flexibility. By default, this function uses an internally optimized Introsort algorithm, with a time complexity typically of $O(N \log N)$. This ensures that it maintains extremely high performance even when processing large datasets.

std::sort accepts an iterator range as a parameter to define the interval to be sorted. Its basic syntax is as follows:

void sort(ForwardIt first, ForwardIt last);

In practical development, we often need to sort based on custom rules; therefore, std::sort supports a second overload version that allows passing a comparator function or a lambda expression.

Example Code:
The following code demonstrates how to arrange an integer vector in descending order:

#include <iostream>
#include <vector>
#include <algorithm> // Header file containing std::sort

int main() {
    std::vector<int> numbers = {5, 2, 9, 1, 5, 6};

    // Default ascending order (smallest to largest)
    std::sort(numbers.begin(), numbers.end());

    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << "\n";

    // Descending order example: using a lambda expression to define custom comparison logic
    std::vector<int> reversed = {5, 2, 9, 1, 5, 6};
    std::sort(reversed.begin(), reversed.end(), 
             [](int a, int b) { return a > b; });

    for (const auto& num : reversed) {
        std::cout << num << " ";
    }
    std::cout << "\n"; // Output: 9 5 6 5 2 1

    return 0;
}

With a custom comparator, std::sort can easily handle complex scenarios such as sorting strings by length or grouping structures by specific fields. It is important to note that this algorithm sorts in-place (In-place), meaning it does not create new containers to store the results, which is particularly important for memory-sensitive applications.

std::find: Quickly Locating Target Elements

Searching for a specific value within a dataset is a high-frequency requirement. std::find is used to search for the first element matching a specified value in an ordered or unordered container. It returns an iterator pointing to that element; if not found, it returns an iterator to the end of the container. This feature allows subsequent logic to uniformly handle cases where the element "exists" or "does not exist".

Example Code:

#include <iostream>
#include <vector>
#include <algorithm> // Header file containing std::find

int main() {
    std::vector<int> data = {10, 23, 45, 78, 99};
    
    int target = 78;

    auto it = std::find(data.begin(), data.end(), target);

    if (it != data.end()) {
        // Target found, output its value and index position (requires manual calculation or combining with other algorithms)
        *it << " found at index: "; 
        for(int i=0; i<data.size(); ++i) {
            if(data[i] == target) std::cout << i; break;
        }
    } else {
        std::cout << target << " not found." << "\n";
    }

    return 0;
}

For more complex search requirements, such as finding an object in a structure array based on ID, std::find is equally applicable. In this case, simply change the third parameter to point to a pointer of that structure:

struct Person { int id; std::string name; };

int main() {
    std::vector<Person> people = {{1, "Alice"}, {2, "Bob"}};
    
    auto it = std::find(people.begin(), people.end(), 
                       [](const Person& p) { return p.id == 2; }); // Note: This syntax is incorrect, see correction below

    // Correct usage requires using a lambda with capture logic or passing a comparator function object
    int targetId = 10086;
    
    auto it_correct = std::find(people.begin(), people.end(), 
                               [](const Person& p) { return p.id == targetId; });

std::transform: A General Solution for Data Mapping and Transformation

std::transform is a powerful tool in the STL used to convert an input sequence into an output sequence. It accepts an iterator range and a function object (Function Object); the return type of this function must be assignable to elements in the target container or stream. This allows developers to write highly abstract data processing logic, such as square roots, modulo operations, string conversions, etc.

Example Code:

#include <iostream>
#include <vector>
#include <algorithm> // Header file containing std::transform
#include <cmath>     // sqrt, pow functions

int main() {
    std::vector<int> squares = {}; 

    for (int i : {10, 23, 45}) {
        int val = static_cast<int>(std::pow(i, 2)); 
        squares.push_back(val);
    }

    // Use transform to map the squared results to another container or directly write to an output stream
    
    std::vector<double> roots;
    
    auto it_begin = squares.begin();
    auto it_end = squares.end();

    while(it_begin != it_end) { 
        double square_root_val = *it_begin; // sqrt function returns a double-precision floating-point number
        
        if(square_root_val > 0){
            std::transform(squares.begin(), squares.end(), roots, [&](int n) -> int{ return static_cast<int>(std::sqrt(n)); });
            
    } else {
        
    }

}