如何用C++的unordered_set判断一个集合包含另一个所有元素
嘿,作为Java转C++的新手,你这个实现思路其实已经非常靠谱了!而且完美契合你看重查找速度、不想用有序集合的需求,先给你点个赞~
首先得说,那个基于hash_set的示例确实已经过时了——hash_set早就是C++标准里被废弃的容器,更关键的是unordered_set本身是无序哈希容器,根本没法用sort(sort要求迭代器支持随机访问,而且排序会直接破坏unordered_set的哈希结构),所以那个方案从根上就不适用于unordered_set。
你的实现逻辑分析
你的循环遍历实现逻辑非常清晰,而且已经做了关键的优化:
- 先做快速失败检查:如果
two的元素数量比one多,直接返回false,这一步能避免很多不必要的遍历操作 - 遍历
two的每个元素,用unordered_set::find检查是否存在于one中,只要有一个元素不存在就立刻返回false,全部存在则返回true
这个逻辑的平均时间复杂度是O(m)(m是two的元素数量),因为unordered_set::find的平均时间复杂度是O(1)——这已经是理论上的最优复杂度了,毕竟你至少得检查two里的每一个元素,不可能比这个更快。
关键优化:把参数改成const引用
不过你的代码有个容易忽略的性能坑:现在的函数参数是值传递:
bool SpecSet::containsAll(unordered_set<Species*> one, unordered_set<Species*> two)
每次调用这个函数都会完整拷贝两个unordered_set,如果集合里元素很多,拷贝的开销会非常大!改成const引用就能彻底避免这个问题:
bool SpecSet::containsAll(const unordered_set<Species*>& one, const unordered_set<Species*>& two)
这样函数只会传递集合的引用,不会做任何拷贝,性能会提升一大截。
更简洁的写法:用std::all_of
如果你想让代码更紧凑,可以用C++11引入的<algorithm>库中的std::all_of算法,结合lambda表达式实现,逻辑和你的循环完全一致,只是代码更简洁:
#include <algorithm> // 别忘了包含这个头文件 bool SpecSet::containsAll(const unordered_set<Species*>& one, const unordered_set<Species*>& two) { if (two.size() > one.size()) { return false; } return std::all_of(two.begin(), two.end(), [&one](Species* species) { return one.find(species) != one.end(); }); }
这个写法和你的循环效率完全相同,只是可读性和简洁度更好一些。
总结
你的核心实现逻辑没有任何问题,只要把参数改成const引用,不管是用自己写的循环还是std::all_of,都是高效且符合你需求的最优方案。
内容的提问来源于stack exchange,提问作者Steve W

