std::unique配合erase仅适用于已排序vector的一行去重,它移动重复元素至末尾再删除;未排序时需先sort,保留原序则须用unordered_set+remove_if。

用 std::unique + erase 组合是唯一靠谱的一行解法
不是所有“去重”都能靠一行解决,但对已排序的 std::vector,std::unique 配合 erase 确实能压缩成一行。它不真正删除元素,只是把重复项移到末尾,再靠 erase 删掉那段“脏尾巴”。没排序就直接用?结果不可预测——std::unique 只删相邻重复项。
- 必须先调用
std::sort(如果允许改变顺序),或用std::stable_sort(保留首次出现位置) -
std::unique返回的是新逻辑结尾的迭代器,不是容器大小,漏接这个返回值会导致erase删错范围 - 原地操作,无额外空间开销,时间复杂度 O(n log n)(主要花在排序上)
示例:
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6, 5};<br>std::sort(v.begin(), v.end());<br>v.erase(std::unique(v.begin(), v.end()), v.end()); // 这一行完成去重
想保留原始顺序?别硬套 std::unique,得换思路
原始顺序很重要时,std::unique 直接失效——它只认相邻重复。这时候“一行代码”基本是个幻觉,但可以压成极简两步:用 std::unordered_set 记录见过的值,配合 std::remove_if + erase。
- 不能用
std::set(慢),必须用std::unordered_set,否则去重本身变成 O(n²) -
std::remove_if不真删,只是重排;必须紧跟erase才生效,少一个就白干 - 注意
unordered_set::insert返回pair<iterator bool></iterator>,只看.second就能判断是否首次插入
示例:
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6, 5};<br>std::unordered_set<int> seen;<br>v.erase(std::remove_if(v.begin(), v.end(), [&seen](int x) { return !seen.insert(x).second; }), v.end());
立即学习“C++免费学习笔记(深入)”;
std::unique 报错 “invalid operator==”?检查你的元素类型
自定义结构体或类用 std::unique 时崩了,大概率是没提供可比性支持。它内部依赖 operator== 判断相等,不是靠地址也不是靠 memcmp。
- 如果是
struct,必须显式定义bool operator==(const MyStruct& other) const - 用
std::unique的第三个参数传自定义谓词?可以,但务必确保谓词满足等价关系(自反、对称、传递),否则行为未定义 - 含指针成员的类慎用默认比较——比较的是指针值,不是所指内容
性能陷阱:小数据量别迷信“一行”,手动遍历反而更稳
向量只有几十个元素?用 std::unique 或 unordered_set 可能比手写双循环还慢。哈希表建表、内存分配、缓存不友好,全在拖后腿。
- 元素少于 ~50 个时,用
for嵌套 +std::find判断是否已存在,代码清晰且实际更快 -
std::unique要求随机访问迭代器,但如果你用的是std::list,就得切到unique()成员函数——别混用 - 编译器对
std::unique+erase组合优化有限,别指望它自动向量化
事情说清了就结束。








