C++unique去除相邻重复元素
std::unique 不是“去重神器”:它只删相邻重复,而且得靠你手动收尾
刚学 C++ 时,我翻到 std::unique 这个函数,心里一喜:“终于不用手写循环去重了!”结果第一次用就翻车——原容器里明明有 1,2,2,3,3,3,4,调完 unique 一看,打印出来还是七个数,最后三个居然还是 3,3,4。
后来才明白:std::unique 从不真正“删除”任何元素,它只是把重复项挤到末尾,再返回一个新终点迭代器。它干的活,更像“整理行李箱”:把相同衣服叠在一起塞进角落,但箱子大小没变,你得自己把多出来的空包拉链拉上。
它到底做了什么?
假设你有一段 vector<int> v = {1,2,2,3,3,3,4};
执行:
auto new_end = std::unique(v.begin(), v.end());
此时 v 的内存状态其实是:{1,2,3,4,3,3,4} —— 前四元素是“去重后保留的”,后面三个是被覆盖掉的旧值(具体值取决于实现,但一定不是你想要的“干净结果”)。
而 new_end 指向第 5 个位置(即 v.begin() + 4),告诉你:“从这儿开始,都是冗余的”。
关键点来了:unique 只比较相邻元素,且仅移动值,不改变容器大小。
它不会碰 {2,1,2,1} 这种非相邻重复;也不会主动 erase——那一步,得你亲手补上。
为什么非要配 erase?因为容器不知道你“想留几个”
C++ 容器的 size 是硬指标。unique 返回的迭代器只是逻辑分界线,就像快递员把包裹堆在门口说“这些是你的”,但门框不会自动缩小。
所以标准写法永远是:
v.erase(std::unique(v.begin(), v.end()), v.end());
这行代码的含义很朴实:把 unique 划出的“冗余区”整段切掉。
注意:erase 的第一个参数必须是 unique 的返回值,第二个必须是 end()——少一个字符,程序就可能越界或漏删。
实际踩坑场景:字符串、自定义类型、逆序处理
-
字符串处理:有人想清理
"aaabbbcccaaa"中的连续重复字母,直接unique(s.begin(), s.end())就够了。但若字符串是"abac",它完全不动——因为a和a不相邻。这时候得先sort再unique?别急,那已经不是unique的职责了,那是std::set或std::unordered_set的地盘。 -
自定义类型:比如
vector<Person>按姓名去重。unique默认用==比较,所以你得确保Person重载了operator==,或者传入自定义谓词:auto new_end = std::unique(v.begin(), v.end(), [](const Person& a, const Person& b) { return a.name == b.name; });这里有个隐性前提:数据必须已按
name排好序或至少相邻同名。否则,张三、李四、张三,unique会放过第二个“张三”。 -
逆序去重?不行,但可以倒着走:
unique只向前看相邻项。想保留最后一次出现的重复项(比如{1,2,2,3,2}→{1,3,2}),得先反转,unique,再反转回来。不过这种需求,往往说明该换思路了——用unordered_map记录最后索引更直白。
一个容易被忽略的细节:unique 是稳定操作
它不打乱原有顺序。{5,1,1,2,2,1} 经 unique 后变成 {5,1,2,1,2,1}(前 4 位有效),顺序关系完全保留。这点比某些“先排序再去重”的方案更尊重原始语义——比如日志时间序列里,你只想合并连续相同的事件状态,但绝不能让“启动→运行→运行→停止”变成“启动→停止→运行”。
别把它当万能解药:什么时候该换别的工具?
- 要全局去重(不管是否相邻)→ 用
std::set或std::unordered_set构建新容器。 - 要统计频次 →
std::map或std::unordered_map。 - 要保序去重且数据量小 → 手写
vector遍历 +find,反而更清晰。 unique的真正主场,永远是:你明确知道重复只发生在连续位置,且不想额外开空间、不介意原地修改。日志压缩、协议帧解析、文本行预处理……这些地方它快、轻、无感。
最后一句实在话
std::unique 不是语法糖,它是 C++ 对“局部整理”这件事的精准抽象。它不替你做决定,但给你一把趁手的尺子和剪刀。
用对了,一行代码解决十年老问题;用错了,你会在调试器里盯着那串“怎么还有重复”的输出,怀疑人生。
下次看到它,别急着复制粘贴——先问自己:这些重复,真的挨着吗?我准备好擦屁股(erase)了吗?我需要的,真的是“挤走重复”,而不是“彻底不要重复”?
答案清楚了,代码自然就稳了。


还没有评论,来说两句吧...