std::unique仅去除相邻重复元素且不改变容器大小,需配合erase使用;要求输入已排序或相同元素相邻;支持自定义谓词比较;不适用于map/set等关联容器;全局去重应使用set或unordered_set。

std::unique 只能去重相邻重复元素,不是全数组去重
它不会改变容器大小,只是把重复元素“挤到后面”,返回一个新末尾迭代器。你得手动用 erase 删掉多余部分,否则数据还在内存里——这不是 bug,是设计如此。
常见错误现象:std::unique(v.begin(), v.end()) 调用后打印 vector,发现重复项还在;或者只遍历到原 size,根本没看到“去重效果”。
- 必须配合
erase使用:v.erase(std::unique(v.begin(), v.end()), v.end()) - 输入必须已排序(或至少相同元素相邻),否则
std::unique会漏掉非相邻重复项 - 对
std::list有专用成员函数unique(),不用配erase,但同样只处理相邻重复
std::unique 的比较逻辑由第三个参数控制,不写就是默认 ==
它默认用 operator== 比较相邻两个元素,但你可以传自定义谓词,比如忽略大小写、按某个成员比较、或判断“近似相等”。注意:这个谓词接收两个参数(a, b),返回 true 表示“视为重复”,即 a 应该被移除。
使用场景:处理结构体数组时,按 id 去重;或浮点数序列中,把差值小于 1e-5 的相邻值当重复。
立即学习“C++免费学习笔记(深入)”;
- 传 lambda 示例:
std::unique(v.begin(), v.end(), [](const auto& a, const auto& b) { return a.id == b.id; }) - 传函数对象或普通函数也行,但参数类型要严格匹配迭代器解引用后的类型
- 不能传
std::equal_to这类模板类实例——它不满足 BinaryPredicate 要求(需可拷贝、可调用)
std::unique 不适用于 map/set 等关联容器
因为 std::unique 要求随机访问迭代器(支持 it + n),而 std::map 的迭代器是双向的,不满足要求。编译会直接报错,典型错误信息:no match for 'operator+' in 'first + __len'。
使用场景:你想对 std::map<:string int></:string> 的 key 去重?没意义——map 本身 key 就不重复。真要“按 value 合并”,得自己遍历统计,或换用 std::unordered_map 手动聚合。
- 对
std::vector<:pair>></:pair>可以用std::unique,只要 pair 可比较且你传对谓词 - 对
std::set,插入时自动去重,无需std::unique - 想对任意容器做“全局去重”,用
std::set或std::unordered_set构造再转回,别硬套std::unique
性能和边界要注意:空容器、单元素、迭代器失效
std::unique 时间复杂度是 O(n),只遍历一次,但前提是你的比较谓词本身是 O(1)。如果谓词里做了深拷贝或字符串查找,整体就变慢了。另外,它不检查迭代器有效性——传 v.end() 和 v.begin() 是合法的,但传 v.begin() - 1 就 UB。
- 空容器或单元素调用安全,返回首迭代器(
v.begin()或v.end()) - 对
std::vector,erase那一步是 O(1) 平摊(移动后续元素),但若在中间大量删除,考虑换成std::remove_if+erase更清晰 - 千万别对
std::vector边遍历边unique——迭代器会失效,结果不可预测
实际用得多的其实是“先 sort 再 unique”,但那是两个独立操作,std::unique 本身从不排序。这点容易混淆,也最容易在调试时卡住半天。










