当前位置:首页 > 技术 > 正文内容

C++ STL 核心算法全景解析与实战指南

访客 技术 2026年6月21日 2

1. 只读与非修改序列操作

此类算法在遍历容器时,仅进行读取或条件判断,绝不会修改底层元素的值或容器的物理结构。

1.1 元素检索 (find 家族)

  • std::find:在区间内线性查找首个与目标值匹配的元素。
  • std::find_if:查找首个使自定义谓词返回 true 的元素。
  • std::find_end:在主干序列中查找子序列最后一次出现的起始位置。
#include <vector>
#include <algorithm>
#include <iostream>

std::vector<int> data_set = {10, 20, 30, 40, 50};

// 精确匹配查找
auto iter = std::find(data_set.begin(), data_set.end(), 30);
if (iter != data_set.end()) {
    std::cout << "Located target: " << *iter << '\n';
}

// 条件谓词查找
auto cond_iter = std::find_if(data_set.begin(), data_set.end(), [](int val) {
    return val > 35;
});
// cond_iter 指向 40

// 子序列查找
std::vector<int> sub_seq = {30, 40};
auto sub_iter = std::find_end(data_set.begin(), data_set.end(), sub_seq.begin(), sub_seq.end());
if (sub_iter != data_set.end()) {
    std::cout << "Subsequence offset: " << std::distance(data_set.begin(), sub_iter) << '\n';
}

1.2 统计与遍历

  • std::count / std::count_if:统计满足特定值或谓词条件的元素数量。
  • std::for_each:对区间内的每个元素执行指定的可调用对象。
std::vector<int> metrics = {12, 45, 12, 89, 12, 33};
int target_count = std::count(metrics.begin(), metrics.end(), 12); 

int high_val_count = std::count_if(metrics.begin(), metrics.end(), [](int m) { 
    return m > 30; 
}); 

std::for_each(metrics.begin(), metrics.end(), [](int& m) { 
    m += 10; // 通过引用修改元素值,但不改变容器结构
});

1.3 比较与断言

  • std::equal / std::mismatch:判断两个序列是否一致,或定位首个差异点。
  • std::all_of / std::any_of / std::none_of:验证区间内元素是否全部、部分或完全不满足特定条件。
std::vector<int> seq_a = {100, 200, 300};
std::vector<int> seq_b = {100, 200, 999};

bool is_identical = std::equal(seq_a.begin(), seq_a.end(), seq_b.begin());

auto mismatch_pair = std::mismatch(seq_a.begin(), seq_a.end(), seq_b.begin());
// mismatch_pair.first 指向 300, mismatch_pair.second 指向 999

std::vector<int> temperatures = {22, 24, 21, 25};
bool all_above_zero = std::all_of(temperatures.begin(), temperatures.end(), [](int t) { return t > 0; });
bool has_freezing = std::any_of(temperatures.begin(), temperatures.end(), [](int t) { return t <= 0; });
bool none_extreme = std::none_of(temperatures.begin(), temperatures.end(), [](int t) { return t > 50; });

2. 序列修改与元素操作

这类算法会直接改变容器内元素的值、顺序,或改变容器的逻辑边界。

2.1 拷贝与转换

std::vector<int> source = {11, 22, 33, 44, 55};
std::vector<int> destination(source.size());

std::copy(source.begin(), source.end(), destination.begin());

std::vector<int> filtered;
std::copy_if(source.begin(), source.end(), std::back_inserter(filtered), [](int v) {
    return v % 2 != 0; // 提取奇数,back_inserter 自动处理扩容
});

std::vector<int> base = {1, 2, 3};
std::vector<int> multiplier = {10, 20, 30};
std::vector<int> product(base.size());

// 双序列转换
std::transform(base.begin(), base.end(), multiplier.begin(), product.begin(), [](int a, int b) {
    return a * b;
});

2.2 替换与移除

std::remove 系列算法并不会真正销毁元素,而是将保留的元素前移,并返回新的逻辑尾迭代器。必须配合 erase 才能真正收缩容器(Erase-Remove 惯用法)。

std::vector<int> values = {5, 10, 15, 10, 20};

std::replace(values.begin(), values.end(), 10, 99); 

std::vector<int> cloned;
std::replace_copy(values.begin(), values.end(), std::back_inserter(cloned), 99, 0); 

// Erase-Remove 惯用法
auto new_logical_end = std::remove(values.begin(), values.end(), 99);
values.erase(new_logical_end, values.end());

values = {2, 4, 6, 7, 8, 9};
values.erase(
    std::remove_if(values.begin(), values.end(), [](int v) { return v % 2 == 0; }),
    values.end()
);

2.3 重排操作

std::vector<int> raw_data = {1, 1, 2, 3, 3, 3, 4};
auto unique_end = std::unique(raw_data.begin(), raw_data.end());
raw_data.erase(unique_end, raw_data.end()); // 去除相邻重复项

std::reverse(raw_data.begin(), raw_data.end());

std::vector<int> ring = {10, 20, 30, 40, 50};
std::rotate(ring.begin(), ring.begin() + 3, ring.end()); // 40 成为首元素

#include <random>
std::vector<int> deck = {1, 2, 3, 4, 5, 6};
std::mt19937 rng(std::random_device{}());
std::shuffle(deck.begin(), deck.end(), rng); // 现代 C++ 随机打乱

3. 排序、检索与合并

3.1 排序算法

std::vector<int> scores = {85, 92, 78, 92, 88};
std::sort(scores.begin(), scores.end(), std::greater<int>()); // 降序排列

struct Record { int id; int score; };
std::vector<Record> records = {{1, 90}, {2, 80}, {3, 90}};
std::stable_sort(records.begin(), records.end(), [](const Record& a, const Record& b) {
    return a.score > b.score; // 保证相同分数的 id 相对顺序不变
});

std::vector<int> pool = {45, 12, 89, 33, 67, 21};
std::partial_sort(pool.begin(), pool.begin() + 3, pool.end()); // 前3个是最小的且有序

// 将第 N 小的元素放置在正确位置,其左侧均小于它,右侧均大于它
std::nth_element(pool.begin(), pool.begin() + 2, pool.end()); 

3.2 二分查找与归并

以下算法要求输入序列必须处于已排序状态,否则会导致未定义行为或错误结果。

std::vector<int> sorted_ids = {101, 104, 104, 108, 110};

bool has_104 = std::binary_search(sorted_ids.begin(), sorted_ids.end(), 104);

auto lower = std::lower_bound(sorted_ids.begin(), sorted_ids.end(), 104); // 指向第一个 104
auto upper = std::upper_bound(sorted_ids.begin(), sorted_ids.end(), 104); // 指向 108

std::vector<int> list_a = {1, 3, 5};
std::vector<int> list_b = {2, 4, 6};
std::vector<int> combined(list_a.size() + list_b.size());
std::merge(list_a.begin(), list_a.end(), list_b.begin(), list_b.end(), combined.begin());

4. 堆结构操作

STL 提供了一套基于迭代器范围的堆操作接口,默认构建大顶堆。

std::vector<int> priority_queue = {30, 10, 50, 20, 40};
std::make_heap(priority_queue.begin(), priority_queue.end());

priority_queue.push_back(60);
std::push_heap(priority_queue.begin(), priority_queue.end()); // 维护堆性质

std::pop_heap(priority_queue.begin(), priority_queue.end()); // 最大值移至末尾
int top_val = priority_queue.back();
priority_queue.pop_back(); // 物理移除

std::sort_heap(priority_queue.begin(), priority_queue.end()); // 堆排序,转为升序序列

5. 极值探测

int x = 15, y = 25;
int minimum = std::min(x, y);
int maximum = std::max({10, 50, 30, 20}); // 支持初始化列表

std::vector<int> sensor_readings = {42, 17, 89, 23, 56};
auto min_iter = std::min_element(sensor_readings.begin(), sensor_readings.end());
auto max_iter = std::max_element(sensor_readings.begin(), sensor_readings.end());

// 一次性获取最小和最大元素的迭代器对
auto bounds = std::minmax_element(sensor_readings.begin(), sensor_readings.end());

6. 数值计算

这些算法定义在 <numeric> 头文件中,专注于数学与统计运算。

#include <numeric>

std::vector<int> cash_flows = {100, -50, 200, -30};
int net_balance = std::accumulate(cash_flows.begin(), cash_flows.end(), 0);

std::vector<int> weights = {1, 2, 3};
std::vector<int> values = {10, 20, 30};
int weighted_sum = std::inner_product(weights.begin(), weights.end(), values.begin(), 0);

std::vector<int> indices(5);
std::iota(indices.begin(), indices.end(), 100); // 填充为 100, 101, 102, 103, 104

std::vector<int> daily_sales = {10, 20, 30, 40};
std::vector<int> cumulative_sales(daily_sales.size());
std::partial_sum(daily_sales.begin(), daily_sales.end(), cumulative_sales.begin()); // 前缀和

std::vector<int> diffs(daily_sales.size());
std::adjacent_difference(daily_sales.begin(), daily_sales.end(), diffs.begin()); // 相邻差分

7. 集合与生成操作

std::vector<int> generated(5);
int seed = 10;
std::generate(generated.begin(), generated.end(), [&seed]() { return seed += 5; });

std::vector<int> base_set = {1, 2, 3, 4, 5};
std::vector<int> sub_set = {2, 4};
bool is_subset = std::includes(base_set.begin(), base_set.end(), sub_set.begin(), sub_set.end());

std::vector<int> set_a = {1, 2, 3, 4};
std::vector<int> set_b = {3, 4, 5, 6};
std::vector<int> union_set, intersect_set, diff_set, sym_diff_set;

std::set_union(set_a.begin(), set_a.end(), set_b.begin(), set_b.end(), std::back_inserter(union_set));
std::set_intersection(set_a.begin(), set_a.end(), set_b.begin(), set_b.end(), std::back_inserter(intersect_set));
std::set_difference(set_a.begin(), set_a.end(), set_b.begin(), set_b.end(), std::back_inserter(diff_set));
std::set_symmetric_difference(set_a.begin(), set_a.end(), set_b.begin(), set_b.end(), std::back_inserter(sym_diff_set));

8. 核心机制与常见陷阱

8.1 排序算法的稳定性差异

std::sort 底层通常采用 Introsort(内省排序),在提供 O(n log n) 平均时间复杂度的同时,不保证相等元素的相对顺序(不稳定)。当业务逻辑要求保留原始插入顺序时(如多级排序),必须使用 std::stable_sort。后者基于归并排序变体,虽然稳定,但通常需要额外的 O(n) 内存空间。

8.2 Erase-Remove 惯用法的底层逻辑

std::remove 并不具备直接调用容器内存释放接口的能力。它的实际行为是将不需要删除的元素向前覆盖,并返回一个新的逻辑边界迭代器。容器的 size() 在此阶段并未改变。只有将该返回的迭代器与 end() 传递给容器的 erase 方法,才能真正销毁多余元素并回收内存。

8.3 有序性前置条件

大量高效算法(时间复杂度低于 O(n))严重依赖输入序列的有序性。这包括二分查找家族(binary_search, lower_bound)、集合运算(set_union, includes)以及 merge。若传入未排序的容器,这些算法不会抛出异常,但会产生完全错误的计算结果或触发未定义行为。在调用前,务必确保数据已完成排序。

标签: C++
返回列表

上一篇:三菱电机工业控制软件基础与应用

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。