std::unique仅去除相邻重复元素且不改变容器物理大小,需配合erase使用;去重前若需处理所有重复项则须先排序,但会破坏原序;保序去重应使用unordered_set;自定义类型需提供比较谓词;适用于string、数组等支持双向迭代器的序列。

std::unique 只能去重相邻重复元素,不改变容器大小
std::unique 不是“去重函数”,它只把**连续重复的元素压缩成一个**,并返回新逻辑结尾的迭代器;原容器物理长度不变,多余元素留在末尾(值未定义,但通常保留原值)。常见错误是调用后直接打印整个容器,结果看到“没删干净”——其实是没配合 erase。
典型用法必须两步走:
- 先用
std::unique移动不重复元素到前端,获取新尾迭代器 - 再用容器的
erase删除从该位置到原结尾的冗余部分
例如:
std::vector<int> v = {1, 1, 2, 2, 2, 3, 4, 4};
auto new_end = std::unique(v.begin(), v.end());
v.erase(new_end, v.end()); // 现在 v 是 {1,2,3,4}
必须先排序才能去所有重复项?不一定,看需求
如果目标是“去除全部重复、只留一个”,std::unique 前必须保证相同元素相邻——最稳妥方式是先调用 std::sort。但注意:排序会改变原始顺序,且时间复杂度升为 O(n log n)。
立即学习“C++免费学习笔记(深入)”;
如果你需要保持首次出现顺序(比如去重但不打乱输入顺序),就不能依赖 std::sort + unique,得换方案:std::unordered_set 记录已见元素,遍历原容器时跳过重复项,写入新容器。
简例(保序去重):
std::vector<int> v = {3, 1, 2, 1, 3, 4};
std::unordered_set<int> seen;
std::vector<int> result;
for (int x : v) {
if (seen.insert(x).second) { // insert 返回 pair<iter, bool>,second 为 true 表示新增
result.push_back(x);
}
}
// result 是 {3,1,2,4}
unique 对自定义类型要自己提供比较逻辑
默认用 operator== 比较相邻元素。若类型没定义 ==,或你想按特定字段判断是否“重复”,就得传入二元谓词(lambda 或函数对象)。
比如按 Person 的 id 去重:
struct Person { int id; std::string name; };
std::vector<Person> people = {{1,"a"}, {1,"b"}, {2,"c"}};
auto new_end = std::unique(people.begin(), people.end(),
[](const Person& a, const Person& b) { return a.id == b.id; });
people.erase(new_end, people.end());
注意:这个 lambda 只比较相邻两个,不负责排序,所以仍要求相同 id 的 Person 在 vector 中原本就相邻。
字符串和数组也能用,但要注意底层约束
std::unique 是基于迭代器的算法,只要支持双向迭代器就能用,包括 std::string、C 风格数组(用指针当迭代器)、std::deque 等。
对 std::string 直接操作很常见:
std::string s = "aaabbbcc"; auto it = std::unique(s.begin(), s.end()); s.erase(it, s.end()); // s 变成 "abc"
对 C 数组需手动算边界:
int arr[] = {1,1,2,2,3};
size_t len = 5;
auto end_it = std::unique(arr, arr + len);
len = end_it - arr; // 新长度
别忘了:无论哪种容器,unique 本身不释放内存,也不调整 size —— 这个责任永远在调用者身上。


















