std::sort + std::unique + erase 是最常用且安全的去重方法;需先排序使相同元素相邻,再用 unique 移动重复项至末尾并返回新逻辑尾,最后 erase 真正删除,时间复杂度 O(n log n),但不保持原序。

用 std::sort + std::unique 是最常用且安全的做法
直接调用 std::unique 不会真正删除元素,它只是把重复项移到末尾,并返回新逻辑结尾的迭代器。必须配合 erase 才算真正移除——这点容易漏掉,导致“以为去重了,实际 vector 长度没变”。
前提是 vector 元素支持比较(如 int、string),且你**不关心原始顺序**:
std::vector<int> v = {1, 2, 2, 3, 3, 4};
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end()); // 现在 v 是 {1,2,3,4}
</int>
-
std::unique要求相同元素连续,所以必须先std::sort - 如果元素是自定义类型,需提供
operator 或传入比较函数给 <code>sort,并确保unique的相等判断逻辑一致(默认用==) - 时间复杂度 O(n log n),空间 O(1)
保持原始顺序时,用 std::unordered_set 辅助遍历
当顺序不能动(比如按输入先后排列的 ID 列表),就得边遍历边记已见元素。用 std::unordered_set 查重平均 O(1),比 std::set 更快:
std::vector<int> v = {2, 1, 2, 3, 1, 4};
std::unordered_set<int> seen;
auto new_end = std::remove_if(v.begin(), v.end(), [&seen](int x) {
if (seen.count(x)) return true;
seen.insert(x);
return false;
});
v.erase(new_end, v.end()); // v 变成 {2,1,3,4}
</int></int>
- 注意
std::remove_if是稳定操作,不破坏剩余元素顺序 -
seen必须捕获为引用([&seen]),否则每次 lambda 都用空集合 - 如果元素不可哈希(如自定义类),得改用
std::set,但插入变 O(log n),整体退化到 O(n log n)
误用 std::unique 不加 sort 的典型错误
常见错误写法:v.erase(std::unique(v.begin(), v.end()), v.end()) 直接套用——这只会去掉「相邻重复」,不是全量去重。
例如 v = {1,2,1,2},执行后仍是 {1,2,1,2},因为没有相邻相等对。这种行为常被误解为“没生效”,其实是语义理解偏差。
-
std::unique的设计初衷就是处理已排序或天然连续重复的数据流(如日志中连续报错) - 调试时可打印
std::distance(v.begin(), std::unique(...))看它声称的新长度,对比v.size()就能发现是否真有收缩 - 别依赖
unique实现“去重”而不检查前提条件
性能与值语义:避免对大对象盲目去重
如果 vector 存的是大结构体或字符串,sort 和 erase 可能触发大量拷贝。这时应优先考虑:
- 改用
vector<const t></const>或vector<:shared_ptr>></:shared_ptr>去重,再重建索引 - 用
std::unordered_set<t></t>先 collect 唯一值,再构造新 vector(更清晰,且避免中间状态混乱) - 确认是否真的需要原地修改——很多场景下生成新 vector 更安全、易读
去重逻辑本身不复杂,但“要不要保序”“元素能否排序/哈希”“对象拷贝代价”这三个点一旦忽略,后续 debug 成本远高于初始选型时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











