一、基础原理与目标
std::unique 的工作原理
std::unique 会扫描序列中相邻的重复元素,将第一个出现的位置保留,将后续的重复元素“挪到”序列末尾,并返回新的结束迭代器。它不会改变未重复元素的相对顺序,前提是重复项是相邻的。
使用 std::unique 的关键在于获得一个新的结束迭代器,然后通过 vector::erase 将尾部的重复部分删除,最终得到一个没有重复元素的容器。这个过程的核心思想是:先在原地对齐重复项,再统一回收末尾多余的元素。
erase 的作用与必要性
erase 删除指定区间内的元素,并返回删除后新的迭代器。对于 std::vector 来说,逐个删除会触发多次复制与容量移动,但使用区间删除(erase(it, end))的方式可以一次性裁剪尾部,从而实现高效收缩。

在与 std::unique 组合时,正确的模式通常是:先得到新的结束迭代器,然后执行 v.erase(new_end, v.end()),从而将重复项从向量中永久移除。这个步骤是实现高效去重的关键环节。
二、将 unique 与 erase 组合在一起的核心思路
组合的实现步骤
最常见的实现思路是:先对容器进行排序,使所有重复项成为相邻的一组;再用 std::unique 将重复项聚集到末尾;最后用 vector::erase 收缩容器到新边界。
这个流程的核心是把“去重逻辑”拆分成两个阶段:对齐重复项和回收尾部。通过这种分解,可以用标准库提供的内置操作来实现高效且简洁的代码。若需要对大规模数据进行去重,这种方式往往性能优于逐元素删除的自定义逻辑。
排序去重与保序去重的权衡
在多数场景中,排序后去重是最直接的高效做法,因为排序提供了快速的重复项聚集;不过这会改变原始的元素顺序。如果需要保持原始出现顺序,则需要采用在遍历时使用 哈希集合 的策略来过滤重复项,然后再将结果拷贝回原容器。
因此,选择哪种策略取决于对顺序的要求以及数据规模。排序去重通常更快且实现更简单;保序去重则需要额外的内存并可能写得更复杂,但能保留原始序列的先后顺序。
三、实战代码示例
示例 1:排序后去重(简洁高效)
下面给出最常用的实现模式,利用 排序、std::unique 与 erase 的组合来实现高效去重;需要注意的是,该方法会改变原始的元素顺序。
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<int> v = {3, 1, 2, 3, 2, 4, 1};// 1) 排序使重复项相邻std::sort(v.begin(), v.end());// 2) 去重并获得新结束迭代器auto it = std::unique(v.begin(), v.end());// 3) 裁剪尾部多余的重复区域v.erase(it, v.end());for (int x : v) std::cout << x << ' ';std::cout << '\\n';return 0;
}
示例 2:保持原始顺序的去重
如果需要保持元素的原始出现顺序,可以采用逐元素检查与删除的方式,借助 unordered_set 来判定是否遇到重复项;在遍历过程中直接对 重复项 进行擦除,确保顺序不被打乱。
#include <iostream>
#include <vector>
#include <unordered_set>int main() {std::vector<int> v = {3, 1, 2, 3, 2, 4, 1};std::unordered_set<int> seen;for (auto it = v.begin(); it != v.end();) {if (!seen.insert(*it).second) {it = v.erase(it);} else {++it;}}for (int x : v) std::cout << x << ' ';std::cout << '\\n';return 0;
}
四、其他相关要点与高级用法
对自定义类型的处理与比较
对于自定义类型 T,如果打算使用排序来实现去重,需要提供一个能用于 operator< 的比较器;若使用 std::unique 的二元谓词来进行去重,则应提供自定义的比较函数,例如 std::unique(first, last, comp),其中 comp 是用于判断等价的谓词。
在实现时,记得将对比逻辑和哈希逻辑保持一致,否则可能出现不可预期的去重结果。对于需要保持稳定性和可预测性的场景,优先选择明确的谓词或自定义哈希函数。
性能与内存的权衡
排序的时间复杂度通常为 O(n log n),随后 unique 与 erase 的总复杂度为 O(n),因此总体复杂度在大量重复数据时往往较优。若需要保序,内存占用会增加,因为需要一个额外的集合来记录已出现的元素。
在实际工程中,选择哪种实现应基于数据规模、是否需要保持顺序以及对性能的实际需求,进行权衡与评估。
与其他去重方案的对比
除 std::unique + erase 外,还有基于哈希表的去重、基于位图的去重等方法。在需要快速去重且对顺序不敏感时,排序+去重通常是最直接有效的方案;若数据类型不具备可排序性,又需要高效去重,使用哈希集合进行去重是常见替代。


