如何判断C++结构体是否在set中?为何需定义<而非==?
嘿,这个问题问得非常到位!我来帮你把这事儿掰扯明白~
首先:为什么std::set必须要有<运算符(或者自定义比较器)?
C++标准库中的std::set默认是基于红黑树(一种平衡二叉搜索树)实现的。这种数据结构的核心优势是能在O(log n)时间内完成插入、删除和查找操作,但它有个硬性要求:必须能对元素进行排序比较,维护严格的有序性。
默认情况下,set会使用<运算符来建立这种排序规则——它需要通过比较元素的“大小”来决定每个元素在树中的位置,查找元素时也是通过不断用<判断往左还是往右遍历树,最终定位目标。
那为什么光有==不行?因为==只能判断两个元素是否完全相等,但红黑树在查找元素的过程中,需要的是“导航”能力:比如要找myfoo,它得知道myfoo比当前节点的元素小还是大,才能决定下一步往哪个分支走。只有==的话,它没办法完成这个导航过程,自然也就没法高效定位元素了。
如何正确定义<运算符?
定义<运算符时,必须满足严格弱序(Strict Weak Ordering)的规则,这是红黑树能正常工作的关键。简单来说,规则包括:
- 任何元素x,
x < x必须返回false(不能自己小于自己) - 如果
x < y为true,那么y < x必须为false(不对称) - 如果
x < y且y < z,那么x < z必须为true(传递性) - 如果x不小于y,y也不小于x,那么x和y被视为“等价”(set会认为它们是同一个元素,不会重复插入)
举个实际的例子,假设你的foo结构体有两个成员:
struct foo { int id; std::string name; // 重载<运算符,遵循严格弱序 bool operator<(const foo& other) const { // 先按id比较,id小的在前 if (id != other.id) { return id < other.id; } // id相等时,按name的字典序比较 return name < other.name; } };
这里要注意:运算符函数必须是const的!因为std::set中的元素是不可修改的(修改会破坏有序性),所以比较操作必须保证能对const的foo对象调用。
如果你不想重载成员运算符,也可以自定义一个比较器结构体,传给set:
struct FooComparer { bool operator()(const foo& a, const foo& b) const { if (a.id != b.id) { return a.id < b.id; } return a.name < b.name; } }; // 使用自定义比较器的set std::set<foo, FooComparer> my_set;
额外小提示
当你定义好<运算符(或自定义比较器)后,就可以用my_set.find(myfoo) != my_set.end()来判断myfoo是否存在于set中了——find方法会利用比较规则高效定位元素,本质上是通过排序比较来确定等价元素的位置,而不是直接用==。
内容的提问来源于stack exchange,提问作者Riemann-bitcoin.

