You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C++11中是否存在类似std::unique但保留等价序列最后元素的函数?

解决C++11中保留等价序列最后一个元素的去重问题

首先明确说:C++11标准库确实没有像你说的那种直接替代std::unique、保留等价序列最后一个元素的函数,但我们可以用几种简单的方式自己实现,完全适配你的需求——毕竟你的deque已经按轻量级字段排好序,等价元素都是连续的,这给我们的实现省了不少事。

方法一:借助标准库函数组合实现(最简洁)

思路很巧妙:既然std::unique是保留等价组的第一个元素,那我们先把容器反转,让原来的最后一个元素变成第一个,用std::unique去重后再反转回来,就得到了保留原序列最后一个元素的结果。

代码示例(假设你的重量级对象是HeavyObject,轻量级字段是key):

#include <algorithm>
#include <deque>

struct HeavyObject {
    int key;
    // 你的其他重量级成员变量
};

int main() {
    std::deque<HeavyObject> dq = { /* 初始化你的数据 */ };

    // 第一步:反转容器,把原序列的最后一个等价元素放到组的开头
    std::reverse(dq.begin(), dq.end());
    // 第二步:用std::unique去重,此时保留的是反转后的第一个(原序列的最后一个)
    auto last_valid = std::unique(dq.begin(), dq.end(),
        [](const HeavyObject& a, const HeavyObject& b) {
            return a.key == b.key; // 自定义等价判断逻辑
        });
    // 移除去重后的无效元素
    dq.erase(last_valid, dq.end());
    // 第三步:再次反转,恢复原序列的顺序
    std::reverse(dq.begin(), dq.end());

    // 现在dq里就只保留每个等价组的最后一个元素了
    return 0;
}

这个方法的优点是完全复用标准库函数,代码简洁易读;缺点是需要两次反转操作,不过对于deque来说,反转的时间复杂度是O(n),且元素交换的开销如果是移动语义的话也不大——如果你的HeavyObject支持移动构造/赋值,那基本没什么负担。

方法二:手动反向遍历实现(更灵活)

如果你不想做两次反转,也可以手动从后往前遍历容器,收集需要保留的元素,最后整理成目标序列。这种方式更灵活,也能避免反转的开销:

#include <deque>
#include <utility> // 用于std::move

struct HeavyObject {
    int key;
    // 你的其他重量级成员变量
};

int main() {
    std::deque<HeavyObject> dq = { /* 初始化你的数据 */ };
    std::deque<HeavyObject> filtered_dq;

    if (dq.empty()) {
        dq.swap(filtered_dq);
        return 0;
    }

    // 先把最后一个元素(肯定要保留)加入新容器
    filtered_dq.push_back(std::move(dq.back()));
    int last_key = filtered_dq.back().key;

    // 从倒数第二个元素开始反向遍历
    for (auto it = std::prev(dq.end(), 2); ; --it) {
        // 遇到不同的key,说明进入了新的等价组,保留这个元素
        if (it->key != last_key) {
            filtered_dq.push_back(std::move(*it));
            last_key = it->key;
        }
        // 遍历到开头就停止
        if (it == dq.begin()) break;
    }

    // 因为是反向收集的,所以要反转恢复顺序
    std::reverse(filtered_dq.begin(), filtered_dq.end());
    // 替换原容器,避免额外拷贝
    dq.swap(filtered_dq);

    return 0;
}

这个方法的好处是只需要一次反转(或者你也可以用插入到开头的方式,但deque头部插入是O(1),不过频繁插入开头可能不如最后反转高效),而且全程用移动语义处理重量级对象,开销很小。

注意事项

不管用哪种方法,都要确保你的容器已经按轻量级字段排序,保证等价元素是连续的——这是所有类似std::unique的去重逻辑生效的前提,你已经提到满足这个条件,所以没问题。

另外,如果你的等价判断逻辑不是简单的相等(比如有自定义的比较规则),只需要把lambda里的判断换成你的自定义谓词就行,和std::unique的谓词要求一致:返回true表示两个元素等价,需要去重。

内容的提问来源于stack exchange,提问作者Kevin Hencke

火山引擎 最新活动