不能直接用 std::unique 去重,因为它只移除相邻重复元素,对非相邻重复项(如 {"a", "b", "a"})无效;若先排序再 unique 则破坏原始顺序,违背日志、调用链等语义要求。

直接用 std::unordered_set 辅助遍历去重,是保序、高效、可预测的解法。 其他方法要么打乱顺序(std::sort+std::unique),要么性能不可控(反复 erase 导致 O(n²)),要么不适用(std::set 强制排序且插入 O(log n))。
为什么不能直接用 std::unique 去重?
std::unique 只移除**相邻重复元素**,不保证全量去重。它不查哈希表,也不记历史,只比当前和前一个值是否相等。
- 输入
{"a", "b", "a"}→ 输出仍是{"a", "b", "a"}(因为两个"a"不相邻) - 常见误用:对未排序的
std::vector<int></int>或std::string直接调unique,结果“没去重” - 若真要用
unique,必须先sort,但排序会破坏原始语义顺序——比如日志序列、API 调用链、配置项列表,顺序即含义
用 std::unordered_set 边扫边记的实操要点
核心是「见过谁」逻辑:遍历时查集合,新元素才保留,时间复杂度稳定 O(n),空间 O(k)(k 为不同元素个数)。
- 用
std::unordered_set,别用std::set:前者平均查找/插入O(1),后者O(log n)且自带排序开销 - 对
std::string或自定义类型,确保支持哈希:要么特化std::hash,要么传入哈希函数对象,否则编译失败 - 避免
push_back触发多次内存重分配:result.reserve(vec.size())预分配最坏情况所需容量 - UTF-8 字符串不能直接用
std::unordered_set<char></char>:char级判重会切碎多字节字符,应先转为std::u8string或按 code point 切分
std::remove_if + unordered_set 原地修改的陷阱
看似节省空间,实则隐含风险:lambda 捕获 seen 后,在 remove_if 中修改容器时,迭代器失效规则仍需遵守;更关键的是,remove_if 的谓词行为必须幂等且无副作用——而 seen.insert(x) 是有状态写入,一旦算法内部重试或并行化(如未来标准扩展),逻辑就不可靠。
- 推荐明确分离:读原容器 → 写新容器,语义清晰、调试友好、线程安全边界明确
- 若真要原地,优先用双指针(尤其针对已排序数组),而非依赖
remove_if的实现细节 - 注意
remove_if返回的是新逻辑尾,必须配对erase,漏掉这步容器大小不变,只是内容被覆盖
真正容易被忽略的点不是语法,而是数据语义:顺序是否承载业务含义?字符串是 ASCII 还是 UTF-8?类型有没有正确定义哈希?这些决定了你该用哪条路径,而不是哪个函数“看起来更短”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











