C++11中是否存在类似std::unique但保留等价序列最后元素的函数?
首先明确说: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




