std::unique仅移除相邻重复元素且不改变容器大小,必须配合erase使用才能真正删除冗余数据;正确用法是erase-remove惯用法:v.erase(std::unique(v.begin(), v.end()), v.end())。

std::unique只能去重相邻重复项,不是真正的“去重”
很多人以为 std::unique 能像 Python 的 set() 那样抹掉所有重复元素,结果发现数组里还剩着旧值、长度也没变——这是因为它只移除**连续重复的元素**,且不改变容器大小,只是把唯一元素“往前挤”,返回一个指向新逻辑尾部的迭代器。真正删掉后面冗余数据,得配合 erase 才行。
典型错误写法:std::unique(v.begin(), v.end()) 单独调用后就不管了,导致后续遍历时看到残留垃圾值。
- 必须先排序(如果想全局去重),再
unique,否则只对已排好序或天然相邻重复的数据有效 -
std::unique返回的是新末尾迭代器,不是新 size,别直接拿v.size()当结果长度用 - 对
vector、string等支持erase的容器,推荐用 erase-remove 惯用法
正确用法:erase-remove 惯用法一步到位
最常用也最安全的写法是把 std::unique 和 erase 组合起来,利用它返回的迭代器精准截断:
std::vector<int> v = {1, 2, 2, 3, 3, 3, 4};
auto new_end = std::unique(v.begin(), v.end());
v.erase(new_end, v.end());</int>
也可以写成一行(更常见):
v.erase(std::unique(v.begin(), v.end()), v.end());
- 这个组合对
vector、deque、string都适用 - 不能用于
list—— 它有自己更高效的list::unique()成员函数,不用手动 erase - 如果元素类型没有默认相等比较(比如自定义结构体),需传入比较谓词,例如
std::unique(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.id == b.id; })
std::unique 对 string 和数组的特殊处理
std::string 本质是容器,所以同样适用 erase-remove 惯用法;但要注意:C++11 后 std::string 支持 erase 迭代器区间,没问题。而原生数组不行——std::unique 可以在数组上工作(传指针),但没法自动 resize,你得自己记下返回的指针位置:
int arr[] = {1,1,2,2,3};
int* end = std::unique(arr, arr + 5);
// 此时 [arr, end) 是去重后有效段,长度为 end - arr
- 对 C 风格数组,
std::unique返回的是新尾指针,不是长度,别漏算end - arr -
std::string调用后若要获取长度,别用s.length(),要用s.erase(...).length()或先保存迭代器再算距离 - 注意
std::unique不保证稳定性以外的顺序——它只保证相对顺序不变,但不会按出现频次或大小重新排列
性能和边界情况:空容器、单元素、全相同
std::unique 在这些情况下行为明确,但容易被忽略:
- 空容器或单元素:返回首迭代器(或尾迭代器),
erase后无副作用,安全 - 全相同元素(如
{5,5,5,5}):返回第二个位置的迭代器(即第一个唯一项之后),erase后只剩一个 - 时间复杂度是 O(n),只做一次遍历,不分配内存,比建 set 再重构快得多
- 如果数据本就高度重复且相邻(比如日志流中连续相同状态),
unique是最优解;但如果重复分散,先sort再unique的代价可能高于直接用unordered_set过滤
真正容易出错的地方,是忘了它只看相邻、不改 size、也不动原始容器结构——这三个点没意识到,八成会写出带 bug 的代码。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











