保序去重应使用std::unordered_set遍历实现,而非std::sort+std::unique;前者时间复杂度o(n)、保持首次出现顺序,后者会打乱原序且仅适用于顺序无关场景。

直接用 std::sort + std::unique 会改变原字符串顺序,不适合「删除重复字符但保持首次出现顺序」的需求
很多人看到“去重”第一反应是 sort + unique,但这组组合只适用于「结果顺序不重要」的场景。对字符串 "abacbad",sort 后变成 "aaabbcd",再 unique 得到 "abcd" —— 字符没重复了,但原始顺序全丢了。如果你要的是 "abcd"(保留首次出现位置),那这方法就错了。
真正要保持顺序的去重,核心是记录「见过谁」,而不是靠排序。
用 std::unordered_set 遍历去重,才是保序解法
边扫边记,遇到新字符就保留,已见的跳过。时间复杂度 O(n),空间 O(k)(k 为不同字符数),且不修改原串结构。
实操建议:
- 用
std::string构造结果串,避免反复push_back引发多次内存重分配,可先reserve原串长度 -
std::unordered_set<char></char>查找平均 O(1),比std::set更合适(无需排序需求) - 注意:ASCII 字符用
char没问题;若含 Unicode(如 UTF-8 多字节),需先做编码切分,不能直接按char判重
std::string removeDuplicates(const std::string& s) {
std::unordered_set<char> seen;
std::string result;
result.reserve(s.size()); // 预分配,防扩容
for (char c : s) {
if (seen.find(c) == seen.end()) {
seen.insert(c);
result.push_back(c);
}
}
return result;
}</char>
std::unique 本身不删除元素,只是把重复项移到末尾
这是最常被误解的一点:unique 不是“删掉重复”,而是“重排 + 返回新逻辑终点”。它要求输入已排序(或至少相同元素连续),否则行为未定义。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
常见错误现象:
- 对未排序字符串直接调
std::unique(s.begin(), s.end()),返回迭代器指向某个位置,但后续没用erase截断,结果看起来“没变化” - 调完
unique忘了erase,字符串长度不变,只是后面一堆脏数据 - 对
std::string用unique时传错范围,比如漏写.begin()/.end(),编译不过或静默出错
正确用法(仅限允许乱序场景):
std::string s = "abacbad"; std::sort(s.begin(), s.end()); // 先排好:"aaabbcd" auto last = std::unique(s.begin(), s.end()); // 移动后:"abcdxxx",last 指向 'd' 后 s.erase(last, s.end()); // 真正删掉后面
性能与边界要注意的几个点
实际项目中容易忽略的细节:
- 空字符串、单字符、全相同字符(如
"aaaa")都要能正确处理——上面unordered_set版本天然支持 - 区分大小写?
"A"和"a"默认算不同字符;如需忽略,插入前统一转小写(用std::tolower,注意 locale 安全) - 如果字符串超长(百万级),
unordered_set的哈希冲突可能轻微影响性能,但通常远好于排序的 O(n log n) - 多线程环境下,若多个线程共用同一
unordered_set,必须加锁;但去重本身是纯函数操作,推荐传值或局部构造
保序去重没有银弹,unordered_set 是平衡简洁性、效率和可读性的合理选择;而 sort+unique 只在你明确接受顺序重排时才该出现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










