C++ STL 去重与合并算法精讲:unique去重、erase-unique套路、merge有序合并、集合运算全套原理与工程实战
2026/6/12 16:50:49 网站建设 项目流程

0. 前言

我们完成了数据结构顺序表的深度学习,彻底吃透了 vector 底层动态扩容、内存连续、增删移位的核心机制,明白了容器底层数据排布逻辑。今天我们回归 C++ STL 算法体系,继续补齐工程高频算法能力。

在日常业务开发、数据预处理、算法刷题中,数据去重、有序数组合并、集合求交/并/差集是刚需操作。很多开发者处理去重逻辑时,只会手写双层循环暴力去重,不仅代码冗余、可读性差,时间复杂度 O(n²) 极易超时,还容易出现边界处理漏洞。

STL 为有序数据处理提供了一套成熟、高效、工业级的算法体系:unique 相邻去重、merge 双有序数组合并、set 系列集合运算,全部基于 O(n) 或 O(nlogn) 最优复杂度实现。其中sort + unique + erase是 C++ 通用去重的标准套路,也是面试、刷题、工程开发的万能模板。

绝大多数初学者存在严重认知误区:认为 unique 可以直接无序去重、可以直接删除容器元素,导致代码逻辑错乱、去重失效、数据残留。本质是不了解 unique 的底层工作机制、适用前提、返回值含义。

今天我们全方位精讲 STL 去重、合并、集合三大类高频算法,从零拆解底层原理、标准写法、易错点、复杂度分析、工程实战场景、面试核心考点,彻底掌握有序数据预处理的全套最优解法。

1. 核心前置认知:unique 去重底层机制

1.1 unique 核心特性(必考)

很多人用不好 unique,根源是不懂它的核心规则,这里给出唯一正确底层定义

unique 只能去除相邻重复元素,不会直接删除容器元素,仅对有序区间生效,无序容器直接调用会去重失败!

这是所有去重坑点的根源。

1.2 unique 工作原理详解

unique 不会改变容器大小、不会释放内存、不会删除元素,它的真实逻辑是:

1. 遍历迭代器区间,检测相邻重复元素;

2. 将不重复的有效元素,从区间头部开始覆盖式前移

3. 返回值为:去重后有效元素末尾的迭代器

4. 该迭代器往后的元素全部是无效残留数据。

1.3 为什么必须先排序再去重?

unique 仅能去除相邻重复,如果数据无序,重复元素分散在区间各处,无法被识别。

排序的作用:将所有重复元素聚拢相邻,为 unique 去重创造条件

这也是万能去重公式必须包含 sort 的核心原因。

2. 工程万能模板:sort + unique + erase 全局去重

2.1 标准去重流程

C++ 所有序列容器(vector、string、deque)通用全局去重三步曲:

1.sort:排序聚拢所有重复元素;

2.unique:相邻去重,前移有效元素,返回有效末尾迭代器;

3.erase:删除末尾无效残留元素,真正收缩容器大小。

2.2 完整实战代码

#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v = {2,1,2,3,1,3,5,4,5}; // 万能去重三步曲 sort(v.begin(), v.end()); auto new_end = unique(v.begin(), v.end()); v.erase(new_end, v.end()); for(int val : v) { cout << val << " "; } // 输出:1 2 3 4 5 return 0; }

2.3 复杂度分析

sort 排序:O(nlogn),unique 遍历覆盖:O(n),erase 尾部删除:O(1);

整体去重复杂度:O(nlogn),远优于手写双重循环 O(n²)。

3. 高阶用法:自定义规则去重

3.1 unique 谓词重载

unique 支持自定义相等判断谓词,可实现模糊去重、结构体去重、条件去重,适配复杂业务场景。

函数原型:

template <class InputIt, class BinaryPredicate> InputIt unique(InputIt first, InputIt last, BinaryPredicate p);

谓词 p 返回 true 则判定两个元素重复,触发覆盖去重。

3.2 结构体自定义去重实战

#include <iostream> #include <vector> #include <algorithm> #include <string> using namespace std; struct Student { string name; int age; }; int main() { vector<Student> stu = { {"张三", 18}, {"张三", 18}, {"李四", 19} }; // 先自定义排序规则,让重复元素相邻 sort(stu.begin(), stu.end(), [](const Student& a, const Student& b){ if(a.name == b.name) return a.age < b.age; return a.name < b.name; }); // 自定义去重:姓名+年龄相同即为重复 auto new_end = unique(stu.begin(), stu.end(), [](const Student& a, const Student& b){ return a.name == b.name && a.age == b.age; }); stu.erase(new_end, stu.end()); for(auto& s : stu) { cout << s.name << " " << s.age << endl; } return 0; }

4. 有序合并算法:merge 精讲

4.1 核心特性与适用场景

业务中经常需要合并两个有序数组、有序列表,手动合并容易出错、逻辑繁琐。STL merge 是专属双有序区间合并算法

核心前提:两个输入区间必须各自有序

核心优势:O(n) 线性合并,合并后结果依然有序,性能极致高效。

4.2 函数原型与实战

#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> a = {1,3,5,7,9}; vector<int> b = {2,4,6,8,10}; vector<int> res; // 提前开辟足够空间,避免扩容开销 res.resize(a.size() + b.size()); // 合并两个有序区间到res merge(a.begin(), a.end(), b.begin(), b.end(), res.begin()); for(int val : res) { cout << val << " "; } // 输出:1 2 3 4 5 6 7 8 9 10 return 0; }

4.3 自定义排序规则合并

如果是降序有序数组,可传入自定义比较器,保证合并规则与原序列排序规则一致。

merge(a.begin(), a.end(), b.begin(), b.end(), res.begin(), greater<int>());

5. STL 四大集合运算算法(有序区间专属)

STL 针对两个有序序列,提供全套集合运算,刷题、数据比对、差集筛选高频使用,全部 O(n) 复杂度。

5.1 set_union 并集

合并两个集合,去除重复,保留所有不重复元素。

5.2 set_intersection 交集

保留两个集合中共同存在的元素。

5.3 set_difference 差集

保留存在于第一个集合、不存在于第二个集合的元素。

5.4 set_symmetric_difference 对称差集

保留两个集合中互相不共有的元素。

5.5 集合运算通用模板

vector<int> a = {1,2,3,4,5}; vector<int> b = {3,4,5,6,7}; vector<int> res; res.resize(10); // 交集示例 auto end = set_intersection(a.begin(),a.end(),b.begin(),b.end(),res.begin()); res.erase(end, res.end());

所有集合算法必须保证输入区间有序,否则结果错乱。

6. 高频致命坑点汇总(工程必避)

1.无序数据直接调用unique:只能删除相邻重复,零散重复元素残留,去重彻底失效;

2.只调用unique不调用erase:容器大小不变,末尾残留无效脏数据;

3.merge输入无序序列:合并结果完全乱序,算法逻辑失效;

4.集合运算未排序:四大集合算法强依赖有序区间,无序输入结果错误;

5.自定义谓词不匹配:sort与unique/merge比较规则不一致,导致匹配错乱;

6.目标容器空间不足:merge与集合运算写入越界,程序崩溃。

7. 企业级编码规范

1. 全局去重统一使用sort+unique+erase标准模板,拒绝手写循环去重;

2. 复杂结构体去重,保证排序规则与去重谓词逻辑一致

3. 双有序数组合并优先使用 merge,O(n) 最优复杂度,代码极简无bug;

4. 集合比对、数据筛选场景,优先使用 STL 集合四大算法,不重复造轮子;

5. 所有有序算法使用前,必须严格校验输入区间是否有序;

6. 结果容器提前预分配空间,避免频繁扩容,提升批量数据处理性能。

8. 面试满分问答(必背)

Q1:unique 算法的底层原理是什么?为什么不能直接去重?

unique 仅移除相邻重复元素,通过前移覆盖保留有效元素,返回有效区间末尾迭代器,不会删除容器元素、不会收缩容器大小,因此必须配合 erase 清理残留数据。

Q2:为什么去重必须先排序?

unique 只能识别相邻重复元素,排序可以将所有重复元素聚拢相邻,让全局重复全部变为相邻重复,从而实现完整全局去重。

Q3:merge 算法的使用前提和优势?

前提是两个输入区间各自有序;优势是线性时间 O(n) 完成合并,效率极高,合并后序列依然保持有序,是有序数组合并最优解法。

Q4:STL集合运算算法的限制是什么?

set_intersection、set_union 等所有集合算法,仅支持有序输入区间,无序序列运算结果完全错误,这是核心限制条件。

9. 全文总结

今天我们完整吃透了STL 去重、合并、集合运算三大核心算法体系,深度掌握了 unique 底层去重原理、sort-unique-erase 万能去重模板、自定义结构体去重、merge 有序合并、四大集合运算算法。

这套算法是数据预处理的基石,替代了低效的手写循环逻辑,代码更简洁、性能更优、边界更严谨,完美适配刷题、业务数据清洗、批量数据规整场景,是现代 C++ 工程开发必备核心能力。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询