C++中如何使用std::unique实现数组去重_C++配合sort移除重复元素【技巧】

5次阅读

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

C++中如何使用std::unique实现数组去重_C++配合sort移除重复元素【技巧】

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; })必须满足等价性:若 ab 等价、bc 等价,则 ac 也得等价。否则行为未定义。

立即学习C++免费学习笔记(深入)”;

更关键的是:这个谓词只用于判断“相邻两元素是否应合并”,它不负责排序逻辑——所以即使你用 abs(a) == abs(b) 做谓词,也必须保证数组已按绝对值排好序,否则 -2, 3, 2 中的 -22 不相邻,不会被去重。

  • 错误用法: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::uniquestd::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 的谓词,且排序也得用同样逻辑,否则前后不一致。

text=ZqhQzanResources