std::unique仅移除相邻重复元素并返回新逻辑结尾迭代器,不改变容器大小,需配合erase才能真正删除;使用前必须确保元素已排序或相同元素相邻。

unique 函数本身不会真正删除重复元素
std::unique 只是把重复元素“挤到末尾”,并返回一个指向新逻辑结尾的迭代器,原容器 size 不变。它不负责擦除,也不保证容器变短——这是最常被误解的一点。
常见错误现象:vec = {1,1,2,2,3} 调用 std::unique(vec.begin(), vec.end()) 后,vec 可能变成 {1,2,3,2,3}(后两个值未定义但保留),size() 还是 5。
- 必须配合
erase使用才能真正删掉重复项:vec.erase(std::unique(vec.begin(), vec.end()), vec.end()) -
unique要求输入已排序(或至少相同元素连续),否则只对相邻重复有效 - 它比较的是「相邻」元素,不是全局去重;未排序时
{1,2,1}经unique后仍是{1,2,1}
必须先 sort 再 unique 才能实现完整去重
对 std::vector 做全局去重,sort + unique + erase 是标准三连操作。其中 sort 让相同元素相邻,为 unique 创造前提。
示例:
立即学习“C++免费学习笔记(深入)”;
std::vectorv = {3,1,4,1,5,9,2,6,5}; std::sort(v.begin(), v.end()); // → {1,1,2,3,4,5,5,6,9} v.erase(std::unique(v.begin(), v.end()), v.end()); // → {1,2,3,4,5,6,9}
- 时间复杂度:
sort是 O(n log n),unique是 O(n),整体主导项是排序 - 如果原始数据已有部分有序,或允许不稳定顺序,可考虑
std::unordered_set辅助去重(但会丢失顺序且需额外空间) - 自定义类型需提供可比较的
operator(供sort)和相等判断(供unique,默认用==)
unique 对非基本类型要小心相等语义
std::unique 默认用 operator== 判断是否重复,如果你的类没重载 ==,或者重载得不严谨(比如忽略某些成员),就会漏去重或误去重。
- 结构体/类去重前,确认
==行为符合业务意义上的“重复” - 也可传入自定义谓词:
std::unique(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.id == b.id; }) - 注意:谓词参数是相邻两个元素,必须满足等价关系(自反、对称、传递),否则行为未定义
替代方案:用 set 或 unordered_set 一次性去重
如果不需要保留原始顺序,或想避免修改原容器,直接构造 std::set 或 std::unordered_set 更直观:
std::vectorv = {3,1,4,1,5}; std::set s(v.begin(), v.end()); // 自动排序 + 去重 std::vector result(s.begin(), s.end()); // → {1,3,4,5}
-
std::set:O(n log n),结果有序,适合后续还需二分查找的场景 -
std::unordered_set:平均 O(n),无序,内存开销略大,但速度通常更快 - 两者都不改变原
vector,也无需手动erase,但无法控制去重“保留哪个”(总是保留第一个插入的)
sort + unique + erase,如果容器里存的是指针或智能指针,unique 比较的是地址或 get() 值,而不是它们指向的对象内容——这时候就得自己写谓词,而且得确保对象本身的相等逻辑是可靠的。










