You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何使用std::unique()保留至多两个重复元素时首元素不符合预期?

问题:std::unique 实现元素至多保留两次时首元素重复异常

需求与代码

期望遍历vector,每个元素至多保留两次,多余的重复项移除。代码如下:

int main() {

    std::vector<int> vec =  {1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5};

    std::sort(vec.begin(), vec.end()); 
    auto last = std::unique(vec.begin(), vec.end(),[count = 1](const auto& a, const auto& b) mutable
    {
        if(a != b)
        {   
            count = 1;
            return false ; 
        }else{
            return ++count > 2; 
        }
    });
    vec.erase(last, vec.end()); 
    
    for(auto it = vec.begin(); it != vec.end(); ++it){std::cout << *it << '\n'; }
   return 0; 
}

运行结果(不符合预期)

1
1
1
2
3
3
4
5
5

首元素1出现了三次,不符合“至多保留两次”的要求。


问题原因分析

问题出在std::unique的工作机制和你的lambda计数逻辑不匹配:

  1. std::unique的核心逻辑:它的设计目标是移除连续的重复元素,仅保留第一个。带自定义谓词的版本中,谓词返回true表示“两个连续元素是需要去重的等价项”,此时第二个元素会被标记为待移除。

  2. 你的lambda计数逻辑漏洞:

    • 初始count=1,第一次比较前两个1时,++count变为2,返回2>2? false,这告诉std::unique这两个元素不属于重复项,都会被保留。
    • 第二次比较第二个和第三个1时,++count变为3,返回3>2? true,理论上第三个1应该被标记为重复,但由于std::unique的遍历是连续处理相邻元素,第三个1后面紧跟着的还是1,当比较第三个和第四个1时,count继续累加为4,返回true,此时第四个1被标记为重复。
    • 但std::unique并不会主动跳过已标记的重复元素,而是继续用它和下一个元素比较,导致第三个1没有被正确覆盖或移除,最终被保留下来。
  3. 本质矛盾:std::unique不是为“保留最多N次重复元素”设计的,用带mutable的lambda跟踪计数,很容易因为std::unique的内部遍历逻辑(比如重复使用同一个lambda实例、不跳过已标记元素)导致计数混乱,出现不符合预期的结果。


正确实现方式

手动遍历vector,直接跟踪每个元素的出现次数,逻辑更清晰且可靠:

#include <vector>
#include <iostream>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5};

    std::sort(vec.begin(), vec.end());

    if (vec.empty()) {
        return 0;
    }

    auto last = vec.begin();
    int count = 1;
    for (auto it = std::next(vec.begin()); it != vec.end(); ++it) {
        if (*it == *last) {
            if (count < 2) {
                ++last;
                *last = *it;
                ++count;
            }
        } else {
            ++last;
            *last = *it;
            count = 1;
        }
    }
    vec.erase(std::next(last), vec.end());

    for (const auto& num : vec) {
        std::cout << num << '\n';
    }
    return 0;
}

运行结果符合预期:

1
1
2
3
3
4
5
5

内容的提问来源于stack exchange,提问作者Ali Sedighi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:19:51