std::unique不能直接对普通数组去重,它仅压缩连续重复元素并返回新逻辑结尾迭代器;必须先排序,再调用std::unique,最后手动截断冗余部分。

std::unique 不能直接对普通数组去重
std::unique 不是“删除重复值”的函数,它只把**连续重复元素压缩成一个**,并返回新逻辑结尾的迭代器。如果原数组没排序,std::unique 会保留所有非相邻重复项,比如 {1,2,1} 经 std::unique 后仍是 {1,2,1}(因为两个 1 不相邻)。
所以必须先排序,再用 std::unique,否则结果不可靠。
- 对
std::vector:先std::sort(v.begin(), v.end()),再auto it = std::unique(v.begin(), v.end()),最后v.erase(it, v.end()) - 对 C 风格数组(如
int arr[5]):用指针传入,auto it = std::unique(arr, arr + 5),再用it - arr得到有效长度 - 注意:
std::unique不改变容器大小,只是把冗余元素挪到末尾,需手动截断
std::unique 的返回值是“新尾迭代器”,不是新容器
常见错误是以为调用 std::unique 后数组/容器就自动变短了。实际它只重排元素,比如 int a[] = {1,1,2,2,3},执行 std::unique(a, a+5) 后,a 可能变成 {1,2,3,2,3},而返回值是指向第 4 个位置(即第一个冗余元素)的指针。
- 必须配合
erase(容器)或记录长度(数组)才能真正“移除” - 对
std::vector,推荐写法:v.erase(std::unique(v.begin(), v.end()), v.end()) - 对数组,不能删内存,只能用返回值算出不重复元素个数:
int len = std::unique(arr, arr + n) - arr
自定义比较时,std::unique 要求“等价关系”而非简单相等
传入的二元谓词(如 [](int a, int b){ return a == b; })必须满足等价性:若 a 和 b 等价、b 和 c 等价,则 a 和 c 也得等价。否则行为未定义。
立即学习“C++免费学习笔记(深入)”;
更关键的是:这个谓词只用于判断“相邻两元素是否应合并”,它不负责排序逻辑——所以即使你用 abs(a) == abs(b) 做谓词,也必须保证数组已按绝对值排好序,否则 -2, 3, 2 中的 -2 和 2 不相邻,不会被去重。
- 错误用法:
std::unique(v.begin(), v.end(), [](int a, int b){ return abs(a) == abs(b); })而不先按abs排序 - 正确流程:先
std::sort(v.begin(), v.end(), [](int a, int b){ return abs(a) ,再用相同逻辑的谓词调std::unique - 注意:谓词参数顺序是
(first, second),即当前元素和下一个元素
性能与副作用:std::unique 是稳定操作,但会修改原数据
std::unique 是原地算法,时间复杂度 O(n),空间 O(1),但它会移动元素、覆盖旧值。如果你需要保留原始顺序(比如去重但不排序),std::unique 加 std::sort 就不适用。
- 保持原序去重应改用
std::unordered_set辅助遍历,或 C++20 的std::ranges::unique配合视图(但依然要先排序才能语义正确) - 对
std::vector<:string>等类型,std::unique触发大量移动构造,可能比哈希表慢,尤其重复率低时 - 没有 const 版本:不能对
const容器或只读数组使用
最容易被忽略的一点:std::unique 对浮点数去重极危险——由于精度误差,0.1 + 0.2 != 0.3,直接用 == 判等大概率失效;此时必须用带 epsilon 的谓词,且排序也得用同样逻辑,否则前后不一致。









