vector去重需根据需求选择方法:用std::sort+std::unique需定义operator

vector 去重前必须定义 operator== 和 operator
标准库的 std::unique 只能处理相邻重复项,而 std::set 或 std::unordered_set 要求元素可比较或可哈希——如果你的 vector 里存的是自定义结构体(比如 struct Person { std::string name; int age; };),默认没有比较逻辑,直接用 std::sort + std::unique 会编译失败,报错类似:invalid operands to binary expression ('const Person' and 'const Person')。
必须显式提供比较方式:
- 用
std::sort配合std::unique:需定义operator 或传入自定义比较 lambda(按字段全序) - 用
std::unordered_set去重:需定义operator==+ 特化std::hash<person></person>,或传入自定义哈希仿函数 - 最简方案是只写
operator==+std::find扫描,但时间复杂度 O(n²),小数据可用
用 std::sort + std::unique 去重(推荐,O(n log n))
适用于字段组合能自然排序的场景,比如按 name 和 age 共同决定唯一性。注意:该方法会改变原 vector 顺序,且要求所有字段参与比较。
struct Person {
std::string name;
int age;
bool operator// 使用
std::vector<person> v = {{"Alice", 30}, {"Bob", 25}, {"Alice", 30}};
std::sort(v.begin(), v.end());
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());</person>
常见坑:
-
std::tie中字段顺序必须和业务“相等逻辑”一致;若只按name去重,就只写std::tie(name),别多加age - 如果结构体含
float字段,避免直接==比较,改用std::abs(a - b) ,此时 <code>operator 也要对应使用误差比较,否则 <code>std::sort可能行为未定义 -
std::unique不会自动调用析构——若结构体含裸指针或资源句柄,确保拷贝/移动语义安全
用 std::unordered_set 去重(保持原始顺序,O(n) 平均)
想保留第一次出现的元素位置?这是唯一高效方案。但必须提供哈希函数。C++20 起支持 std::hash 对 std::tuple 的特化,可简化写法:
struct Person {
std::string name;
int age;
};
<p>namespace std {
template struct hash<person> {
size_t operator()(const Person& p) const {
return hash<string>{}(p.name) ^ (hash<int>{}(p.age) <p>// 使用
std::vector<person> v = {{"Alice", 30}, {"Bob", 25}, {"Alice", 30}};
std::unordered_set<person> seen;
std::vector<person> unique_v;
for (const auto& p : v) {
if (seen.insert(p).second) { // insert 返回 pair<iterator bool>
unique_v.push_back(p);
}
}</iterator></person></person></person></p></int></string></person></p>
关键细节:
- 哈希函数不能只用
^简单异或(如上例),容易碰撞;生产环境建议用std::hash<:tuple>></:tuple>或boost::hash_combine -
std::unordered_set插入失败时不会抛异常,但若Person析构可能抛异常,需确保noexcept正确标注 - 若结构体含
std::vector<int></int>等容器字段,std::hash默认不支持,得手动展开哈希计算
字段部分参与去重时,别硬套通用算法
比如只想按 name 去重,但保留每个 name 对应的最新 age(按插入顺序或时间戳)。这时 std::unique 和 std::unordered_set 都不够用。
正确做法是手写遍历 + std::find_if 或哈希表记录已见 key:
std::vector<person> v = {{"Alice", 30}, {"Bob", 25}, {"Alice", 35}};
std::unordered_set<:string> seen_name;
std::vector<person> unique_v;
for (auto& p : v) {
if (seen_name.insert(p.name).second) {
unique_v.push_back(p); // 保留首次出现的
}
}</person></:string></person>
或者反向遍历取最后一次:
std::unordered_map<:string person> latest;
for (const auto& p : v) {
latest[p.name] = p; // 自动覆盖
}
std::vector<person> unique_v;
for (const auto& kv : latest) {
unique_v.push_back(kv.second);
}</person></:string>
这种场景下,强行套 std::sort + std::unique 会丢失“最新”语义,也破坏原始顺序,反而更难维护。
真正麻烦的不是语法,而是想清楚:你所谓的“重复”,到底由哪些字段定义?是否允许浮点近似?是否要保序?这些没理清,代码写得再短也没用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











