C++中的unique函数是STL提供的去重算法,定义在<algorithm>头文件中,很多开发者第一次使用时都会踩坑,以为调用一次就能直接得到完全去重后的容器,实际上它的工作逻辑有特定的限制,需要配合其他操作才能达成预期效果。

unique函数的基本用法
unique的函数原型有两种重载形式,最常用的是接受两个迭代器参数的版本,作用是消除容器中相邻的重复元素。它的返回值是一个迭代器,指向去重后容器中最后一个不重复元素的下一个位置。
下面是一个最基础的字符串去重示例:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main() {
std::vector<std::string> vec = {"apple", "banana", "apple", "orange", "banana", "apple"};
// 先排序,让相同元素相邻
std::sort(vec.begin(), vec.end());
// 调用unique去重,返回去重后的尾迭代器
auto new_end = std::unique(vec.begin(), vec.end());
// 输出去重后的元素
for (auto it = vec.begin(); it != new_end; ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
运行上面的代码,输出结果是apple banana orange,可以看到重复的元素已经被去除。但要注意,此时vec的大小并没有改变,只是前面三个位置是不重复元素,后面的位置还是原来的元素,只是被覆盖了而已。
unique函数的核心前置条件
unique函数只能处理相邻的重复元素,这是它最核心的工作逻辑,也是最容易踩坑的点。如果你的容器中重复元素不相邻,直接调用unique是不会有任何去重效果的。
因此使用unique的正确前置步骤是:先对容器元素进行排序,让所有相同的元素相邻排列,再调用unique函数。如果没有排序就直接调用,比如上面的示例如果不先执行std::sort(vec.begin(), vec.end());,输出的结果还是原来的顺序,重复元素不会被去除。
真正完成去重的完整步骤
unique函数本身不会删除容器中的元素,也不会改变容器的大小,它只是把不重复的元素移动到容器的前部,然后返回新的逻辑尾迭代器。如果想要真正得到一个没有重复元素的容器,还需要配合erase方法删除后面的冗余元素。
完整的去重流程代码示例如下:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> nums = {1, 3, 2, 3, 5, 2, 1, 4};
// 第一步:排序,让相同元素相邻
std::sort(nums.begin(), nums.end());
// 第二步:调用unique,得到去重后的尾迭代器
auto unique_end = std::unique(nums.begin(), nums.end());
// 第三步:删除尾迭代器之后的冗余元素
nums.erase(unique_end, nums.end());
// 输出最终去重后的结果
for (int num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "容器大小:" << nums.size() << std::endl;
return 0;
}
运行后输出结果为1 2 3 4 5,容器大小也变成了5,才是真正完成了去重。
常见坑点总结
1. 忘记排序直接调用unique
这是最常见的错误,前面已经提到过,unique只处理相邻重复元素,不排序的话重复元素分散在容器中,调用后不会有任何效果。
2. 忽略unique的返回值
有些开发者调用unique之后就以为去重完成,没有用返回值配合erase删除冗余元素,导致容器大小不变,后续遍历如果还是用原来的end()迭代器,还是会访问到原来的重复元素。
3. 自定义类型未提供合适的比较规则
如果容器里存放的是自定义类型,排序和去重都需要对应的比较规则。排序需要自定义operator<或者传入比较函数,unique默认使用==判断元素是否相等,如果自定义类型没有重载==,也没有给unique传入自定义相等判断函数,会导致编译错误或者判断逻辑不符合预期。
自定义类型的去重示例如下:
#include <iostream>
#include <algorithm>
#include <vector>
struct Person {
std::string name;
int age;
// 重载<用于排序
bool operator<(const Person& other) const {
return name == other.name ? age < other.age : name < other.name;
}
// 重载==用于unique判断相等
bool operator==(const Person& other) const {
return name == other.name && age == other.age;
}
};
int main() {
std::vector<Person> people = {
{"Tom", 18},
{"Jerry", 20},
{"Tom", 18},
{"Tom", 19}
};
std::sort(people.begin(), people.end());
auto unique_end = std::unique(people.begin(), people.end());
people.erase(unique_end, people.end());
for (auto& p : people) {
std::cout << p.name << " " << p.age << std::endl;
}
return 0;
}
4. 迭代器失效问题
如果调用unique之后,又对容器做了插入、删除等操作,之前得到的unique返回的迭代器可能会失效,后续不能再使用这个迭代器进行遍历或者操作,需要重新获取容器的迭代器。
5. 对关联容器使用unique
像std::set、std::map这类关联容器本身就不允许有重复元素,不需要使用unique去重,而且关联容器的迭代器不支持随机访问,也不适合用unique算法操作,强行使用会导致错误。
总结
使用C++的unique函数做去重,核心要记住三个要点:先排序让重复元素相邻,调用unique获取新的尾迭代器,用erase删除冗余元素。同时注意自定义类型的比较规则、迭代器失效等坑点,就能正确高效地使用这个去重算法了。