Exploring Modern C++ through Standard Library Algorithms

Task 1: Sequence Manipulation with Iterators

This task demonstrates the manipulation of container elements using standard algorithms. We will explore how to reverse sequences and perform cyclic rotations on both strings and vectors.

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

// Helper function to display container contents
template <typename Container>
void print_container(const Container& c) {
    for (const auto& item : c) {
        std::cout << item << " ";
    }
    std::cout << "\n";
}

void demo_reversal() {
    std::string original = "0123456789";
    std::cout << "Original string: " << original << std::endl;

    // In-place reversal
    std::string reversed_in_place = original;
    std::reverse(reversed_in_place.begin(), reversed_in_place.end());
    std::cout << "Reversed in-place: " << reversed_in_place << std::endl;

    // Copy reversal
    std::string reversed_copy(original.size(), ' ');
    std::reverse_copy(original.begin(), original.end(), reversed_copy.begin());
    std::cout << "Reversed into copy: " << reversed_copy << std::endl;
}

void demo_rotation() {
    std::vector<int> data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    
    std::cout << "Original vector: ";
    print_container(data);

    // Rotate left by 1
    auto rotated_1 = data;
    std::rotate(rotated_1.begin(), rotated_1.begin() + 1, rotated_1.end());
    std::cout << "Rotated left by 1: ";
    print_container(rotated_1);

    // Rotate left by 2
    auto rotated_2 = data;
    std::rotate(rotated_2.begin(), rotated_2.begin() + 2, rotated_2.end());
    std::cout << "Rotated left by 2: ";
    print_container(rotated_2);
}

int main() {
    demo_reversal();
    demo_rotation();
    return 0;
}

Concept Analysis:

The std::reverse algorithm modifies the sequence in-place, swapping elements from the out side in. In contrast, std::reverse_copy preserves the original source sequence and writes the reversed order to a destination range.

The std::rotate algorithm performs a cyclic left shift. It takes three iterators: first, n_first, and last. The element pointed to by n_first becomes the new first element. The algorithm effectively swaps the range [first, n_first) with [n_first, last). This is useful for shuffling data or implementing queue-like behavior.

Task 2: Data Generation and Statistical Analysis

This section covers generating random data, sorting, and calculating statistical properties like minimum, maximum, and average values. We also examine the efficiency of combining search operations.

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <random>
#include <iomanip>

void demo_sorting() {
    std::vector<int> numbers(10);
    
    // Modern C++ random number generation
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distrib(0, 100);

    // Generate random numbers
    std::generate(numbers.begin(), numbers.end(), [&]() { return distrib(gen); });
    std::cout << "Generated: ";
    for(auto n : numbers) std::cout << n << " ";
    std::cout << "\n";

    // Full sort
    auto sorted_full = numbers;
    std::sort(sorted_full.begin(), sorted_full.end());
    std::cout << "Sorted: ";
    for(auto n : sorted_full) std::cout << n << " ";
    std::cout << "\n";

    // Partial sort (excluding first and last)
    auto sorted_partial = numbers;
    if(sorted_partial.size() > 2) {
        std::sort(sorted_partial.begin() + 1, sorted_partial.end() - 1);
    }
}

void demo_stats() {
    std::vector<int> dataset(10);
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> distrib(0, 100);
    std::generate(dataset.begin(), dataset.end(), [&]() { return distrib(gen); });

    // Finding min and max separately vs simultaneously
    auto min_it = std::min_element(dataset.begin(), dataset.end());
    auto max_it = std::max_element(dataset.begin(), dataset.end());
    
    auto minmax_pair = std::minmax_element(dataset.begin(), dataset.end());

    double sum = std::accumulate(dataset.begin(), dataset.end(), 0.0);
    double mean = sum / dataset.size();
    
    std::cout << "Min: " << *min_it << ", Max: " << *max_it << "\n";
    std::cout << "Mean: " << std::fixed << std::setprecision(2) << mean << "\n";
}

Concept Analysis:

The std::generate algorithm fills a range by calling a provided generator function (or lambda) for each element. This is ideal for populating containers with test data or specific patterns.

Using std::minmax_element is more efficient than calling std::min_element and std::max_element separately. The latter requires two passes over the data (O(2n)), while the former finds both in a single pass (O(n)). This is particularly benfeicial for large datasets.

Lambda expressions (introduced in C++11) allow defining anonymous functions inline. They are perfect for short operations like the generator used here, avoiding the need to define separate named functions outside the main logic.

Task 3: String Transformation

String manipulation often involves case conversion and applying specific character transformations. This task utilizes std::transform to apply operations to characters.

#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>

// Custom transformer: shifts character to next in alphabet (cyclic)
char shift_char(char c) {
    if (c == 'z') return 'a';
    if (c == 'Z') return 'A';
    if (std::isalpha(c)) return c + 1;
    return c;
}

void demo_case_conversion() {
    std::string text = "Hello World 2049!";
    
    std::string lower_text = text;
    std::transform(text.begin(), text.end(), lower_text.begin(), 
                   [](unsigned char c){ return std::tolower(c); });

    std::string upper_text = text;
    std::transform(text.begin(), text.end(), upper_text.begin(), 
                   [](unsigned char c){ return std::toupper(c); });
                   
    std::cout << "Original: " << text << "\n";
    std::cout << "Lower: " << lower_text << "\n";
    std::cout << "Upper: " << upper_text << "\n";
}

void demo_custom_transform() {
    std::string input = "I love cosmos!";
    std::string output(input.size(), ' ');
    
    // Apply custom shift_char function
    std::transform(input.begin(), input.end(), output.begin(), shift_char);
    
    std::cout << "Shifted: " << output << "\n";
}

Concept Analysis:

The std::transform function applies a unary operation to each element in a range and stores the result in a destination range. It takes four arguments: the beginning of the input, the end of the input, the beginning of the output, and the operation function.

If the output iterator points to the same range as the input (in-place), the original data is overwritten. If they point to different containers (or different locations within the same container), the original data is preserved. std::tolower and std::toupper are standard library utilities used for case normalization.

Task 4: Palindrome Detection

This task implements logic to check if a string reads the same forwards and backwards. We will look at both case-sensitive and case-insensitive implementations.

#include <iostream>
#include <string>
#include <cctype>

bool is_palindrome(const std::string& s) {
    size_t left = 0;
    size_t right = s.length() - 1;
    
    while (left < right) {
        if (s[left] != s[right]) {
            return false;
        }
        ++left;
        --right;
    }
    return true;
}

bool is_palindrome_ignore_case(const std::string& s) {
    size_t left = 0;
    size_t right = s.length() - 1;
    
    while (left < right) {
        if (std::tolower(s[left]) != std::tolower(s[right])) {
            return false;
        }
        ++left;
        --right;
    }
    return true;
}

void check_strings() {
    std::string input;
    // Using getline to handle strings with spaces
    std::cout << "Enter a string: ";
    while (std::getline(std::cin, input)) {
        std::cout << std::boolalpha
                  << "Case Sensitive: " << is_palindrome(input) << "\n"
                  << "Case Insensitive: " << is_palindrome_ignore_case(input) << "\n\n";
        std::cout << "Enter a string: ";
    }
}

Concept Analysis:

When handling user input, std::cin >> input reads only until the first whitespace character. To process strings containing spaces (like "nurses run"), std::getline(std::cin, input) must be used. This function reads the entire line until a newline character is encountered.

Task 5: Numeric Base Conversion

This utility converts a decimal integer into a string representation of various bases (binary, octal, hexadecimal, etc.).

#include <iostream>
#include <string>
#include <algorithm>

std::string convert_base(int number, int base) {
    if (number == 0) return "0";
    if (base < 2 || base > 36) return "Invalid Base";

    std::string result;
    bool is_negative = number < 0;
    unsigned int num = is_negative ? -number : number;

    while (num > 0) {
        int remainder = num % base;
        char digit;
        if (remainder < 10) {
            digit = '0' + remainder;
        } else {
            digit = 'A' + (remainder - 10);
        }
        result += digit;
        num /= base;
    }

    if (is_negative) result += '-';

    std::reverse(result.begin(), result.end());
    return result;
}

Task 6: Caesar Cipher Visualization

This task demonstrates how to create a simple substitution cipher table using rotation algorithms.

#include <iostream>
#include <string>
#include <algorithm>

void print_cipher_table() {
    std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
    
    std::cout << "Shift Key\tCipher Text\n";
    
    for (int shift = 1; shift <= 26; ++shift) {
        std::string shifted = alphabet;
        
        // Rotate the alphabet
        std::rotate(shifted.begin(), shifted.begin() + shift, shifted.end());
        
        // Convert to uppercase for display
        std::transform(shifted.begin(), shifted.end(), shifted.begin(), ::toupper);
        
        std::cout << shift << "\t\t" << shifted << "\n";
    }
}

Task 7: Arithmetic Drill Program

This program generates random arithmetic problems and evaluates the user's answers, calculating accuracy.

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <utility>

int main() {
    std::srand(std::time(0));
    int score = 0;
    const int total_questions = 10;

    for (int i = 0; i < total_questions; ++i) {
        int val1 = std::rand() % 10 + 1;
        int val2 = std::rand() % 10 + 1;
        int op_code = std::rand() % 4;
        int solution;
        char symbol;

        switch (op_code) {
            case 0:
                symbol = '+';
                solution = val1 + val2;
                break;
            case 1:
                if (val1 < val2) std::swap(val1, val2);
                symbol = '-';
                solution = val1 - val2;
                break;
            case 2:
                symbol = '*';
                solution = val1 * val2;
                break;
            case 3:
                if (val1 < val2) std::swap(val1, val2);
                // Ensure divisibility for cleaner problems
                if (val2 != 0) val1 = (val1 / val2) * val2; 
                if (val2 == 0) val2 = 1; // Prevent div by zero
                symbol = '/';
                solution = val1 / val2;
                break;
        }

        std::cout << val1 << " " << symbol << " " << val2 << " = ";
        int user_ans;
        std::cin >> user_ans;

        if (user_ans == solution) {
            score++;
        }
    }

    double percentage = (static_cast<double>(score) / total_questions) * 100;
    std::cout << "\nAccuracy: " << std::fixed << std::setprecision(2) 
              << percentage << "%" << std::endl;

    return 0;
}

Tags: C++ STL algorithms iterators vectors

Posted on Fri, 02 Oct 2026 16:22:18 +0000 by HeinekenBeer