STL算法

61 阅读4分钟

C++ STL算法

一、STL算法概述

1. 算法分类体系

  • 非修改序列算法:不改变容器内容(如find、count)
  • 修改序列算法:改变容器内容(如copy、transform)
  • 排序及相关操作:排序、合并、搜索(如sort、merge)
  • 数值算法:数学运算(如accumulate、inner_product)

2. 算法通用特性

  • 基于迭代器工作,不直接操作容器
  • 大多数算法在<algorithm>头文件中定义
  • 数值算法在<numeric>中定义
  • 并行版本(C++17)在<execution>中定义

二、非修改序列算法

1. 查找算法

// 基本查找
auto it = std::find(v.begin(), v.end(), value);

// 条件查找
auto it = std::find_if(v.begin(), v.end(), [](auto& x) { 
    return x > 10; 
});

// 查找子序列
auto pos = std::search(v1.begin(), v1.end(), v2.begin(), v2.end());

// 其他查找
std::adjacent_find  // 查找相邻重复元素
std::find_first_of  // 查找集合中任意元素首次出现
std::find_end       // 查找子序列最后一次出现

2. 统计与比较

// 计数
int cnt = std::count(v.begin(), v.end(), value);
int cnt = std::count_if(v.begin(), v.end(), pred);

// 比较
bool equal = std::equal(v1.begin(), v1.end(), v2.begin());
auto mismatch = std::mismatch(v1.begin(), v1.end(), v2.begin());

// 检查条件
bool all = std::all_of(v.begin(), v.end(), pred);
bool any = std::any_of(v.begin(), v.end(), pred);
bool none = std::none_of(v.begin(), v.end(), pred);

三、修改序列算法

1. 拷贝与移动

// 基本拷贝
std::copy(src.begin(), src.end(), dest.begin());

// 条件拷贝
std::copy_if(src.begin(), src.end(), dest.begin(), pred);

// 反向拷贝
std::copy_backward(src.begin(), src.end(), dest.end());

// 移动元素(C++11)
std::move(src.begin(), src.end(), dest.begin());

2. 填充与生成

// 填充
std::fill(v.begin(), v.end(), value);
std::fill_n(v.begin(), 5, value); // 填充前n个

// 生成
std::generate(v.begin(), v.end(), [](){ 
    return rand() % 100; 
});

// iota生成序列(C++11)
std::iota(v.begin(), v.end(), 10); // 10,11,12...

3. 替换与变换

// 替换
std::replace(v.begin(), v.end(), old_val, new_val);
std::replace_if(v.begin(), v.end(), pred, new_val);

// 变换
std::transform(v1.begin(), v1.end(), v2.begin(), 
    [](auto x){ return x * 2; });

// 二元变换
std::transform(v1.begin(), v1.end(), v2.begin(), 
    v3.begin(), std::plus<int>());

4. 删除算法

// 移除元素(逻辑删除,返回新end)
auto new_end = std::remove(v.begin(), v.end(), value);
auto new_end = std::remove_if(v.begin(), v.end(), pred);

// 实际删除(erase-remove惯用法)
v.erase(std::remove(v.begin(), v.end(), value), v.end());

// 去重
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());

四、排序及相关算法

1. 基本排序

// 快速排序(不稳定)
std::sort(v.begin(), v.end());
std::sort(v.begin(), v.end(), std::greater<int>());

// 稳定排序
std::stable_sort(v.begin(), v.end());

// 部分排序
std::partial_sort(v.begin(), v.begin()+5, v.end()); // 前5个有序

2. 选择算法

// 第n大元素
std::nth_element(v.begin(), v.begin()+n, v.end());

// 分区
auto it = std::partition(v.begin(), v.end(), pred);
auto it = std::stable_partition(v.begin(), v.end(), pred);

// 划分点(已分区范围)
auto it = std::partition_point(v.begin(), v.end(), pred);

3. 合并与堆操作

// 合并有序序列
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin());

// 堆操作
std::make_heap(v.begin(), v.end());
std::push_heap(v.begin(), v.end());
std::pop_heap(v.begin(), v.end());
v.pop_back(); // 实际移除

五、数值算法

1. 基础数值运算

#include <numeric>

// 累加
int sum = std::accumulate(v.begin(), v.end(), 0);
int sum = std::accumulate(v.begin(), v.end(), 1, 
    [](int a, int b){ return a * b; });

// 内积
int product = std::inner_product(v1.begin(), v1.end(), 
    v2.begin(), 0);

// 相邻差
std::adjacent_difference(v.begin(), v.end(), result.begin());

// 部分和
std::partial_sum(v.begin(), v.end(), result.begin());

2. 广义数值运算

// 约简(C++17)
auto result = std::reduce(v.begin(), v.end());

// 变换约简(C++17)
auto sum = std::transform_reduce(v.begin(), v.end(), 
    0, std::plus<>(), [](auto x){ return x*x; });

// 扫描(C++17)
std::inclusive_scan(v.begin(), v.end(), result.begin());
std::exclusive_scan(v.begin(), v.end(), result.begin(), 0);

六、C++17/20新特性

1. 并行算法

#include <execution>

// 并行策略
std::sort(std::execution::par, v.begin(), v.end());
std::for_each(std::execution::par_unseq, v.begin(), v.end(), f);

// 可用策略
- seq    : 顺序执行
- par    : 并行执行
- par_unseq: 并行+向量化

2. 约束算法(C++20)

#include <ranges>

// 范围版本算法
std::ranges::sort(v);
auto it = std::ranges::find(v, value);

// 管道语法
auto result = v | std::views::filter(pred) 
               | std::views::transform(f);

七、算法高级技巧

1. 迭代器适配器

#include <iterator>

// 反向迭代
std::copy(v.rbegin(), v.rend(), std::ostream_iterator<int>(cout, " "));

// 插入迭代器
std::fill_n(std::back_inserter(v), 5, 42);

// 流迭代器
std::copy(std::istream_iterator<int>(cin), 
         std::istream_iterator<int>(),
         std::back_inserter(v));

2. 算法组合模式

// 查找转换模式
auto it = std::find_if(v.begin(), v.end(), pred);
if (it != v.end()) {
    *it = transform(*it);
}

// 擦除-唯一模式
v.erase(std::unique(v.begin(), v.end()), v.end());

// 分区-排序模式
auto mid = std::partition(v.begin(), v.end(), pred);
std::sort(v.begin(), mid);

八、性能优化指南

1. 算法复杂度对比

算法平均复杂度适用场景
std::findO(n)线性搜索
std::binary_searchO(log n)已排序范围
std::sortO(n log n)通用排序
std::stable_sortO(n log n)需要稳定性
std::partial_sortO(n log k)TopK问题
std::nth_elementO(n)中位数/百分位

2. 选择优化策略

  1. 小数据量

    • 使用线性算法(避免排序开销)
    • 简单排序(插入排序)
  2. 大数据量

    • 优先使用O(n log n)算法
    • 考虑并行执行(C++17)
  3. 特定场景

    • 已排序范围使用binary_search
    • 去重先排序后unique

九、常见问题与陷阱

1. 迭代器失效

// 错误示例(在循环中删除)
for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it % 2 == 0) {
        v.erase(it); // 危险!it失效
    }
}

// 正确做法
auto new_end = std::remove_if(v.begin(), v.end(), 
    [](int x){ return x % 2 == 0; });
v.erase(new_end, v.end());

2. 谓词要求

// 纯函数要求(无状态)
bool pred(int x) {
    static int count = 0; // 危险!
    return x > count++;
}

// 排序严格弱序
bool compare(int a, int b) {
    return a <= b; // 错误!必须用 <
}

十、自定义算法实现

1. 实现通用算法模板

template <typename InputIt, typename UnaryPredicate>
InputIt my_find_if(InputIt first, InputIt last, UnaryPredicate pred) {
    for (; first != last; ++first) {
        if (pred(*first)) {
            return first;
        }
    }
    return last;
}

2. 算法策略模式

template <typename RandIt, typename Compare>
void my_sort(RandIt first, RandIt last, Compare comp) {
    if (last - first <= 40) {
        insertion_sort(first, last, comp);
    } else {
        quick_sort(first, last, comp);
    }
}

掌握STL算法需要:

  1. 理解每种算法的适用场景和复杂度
  2. 熟悉迭代器和函数对象的使用
  3. 掌握现代C++的并行和范围特性
  4. 避免常见陷阱和错误用法
  5. 能够组合算法解决复杂问题