C++14 std::map基于tuple部分键实现O(logN)查找的解决方案
我有一个C++14环境下的map:
using MyTuple = tuple<A, B, C>; map<MyTuple, MyData>
其中MyTuple使用默认的operator<(),比较顺序为优先匹配A、其次匹配B、最后匹配C。
我需要匹配固定前缀(a, b)的键,且查找复杂度要求为O(log N),符合条件的键在map中必然是连续存储的,我想要实现的逻辑如下:
map<MyTuple, MyData> my_map = GetMyData(); tuple<A, B> my_key = make_tuple(a, b); auto iter = my_map.lower_bound_if([my_key](const MyTuple& key) { if (get<0>(my_key) == get<0>(key) && get<1>(my_key) == get<1>(key)) { return true; } return false; }); while( /* iter指向的键前缀仍为(a,b) */) { // 处理当前迭代器 // 迭代器自增 }
但map没有lower_bound_if成员函数,map::find和map::lower_bound都要求传入完整的MyTuple参数。我可以构造一个比所有现存C值都小的C值来凑出查询键,但这种方案后续可维护性很差;也可以自己实现查找逻辑,但会依赖当前std::map的具体实现。有没有更合理的解决方案?
问题更新
我最终采纳的方案是使用比较函数中的部分键匹配特性,也就是C++14新增的透明运算符函子。
该方案的核心思路是:如果一组对象按照自身的部分属性排序,比如员工集合按照employee.id排序,我们希望既可以用员工对象查询,也可以直接用id整数值查询,只需要自定义一个包含多组比较逻辑的bool operator()结构体,后续的匹配逻辑会由重载决议自动完成。
在我的场景中,既可以保留map所需的严格全序比较逻辑,也可以额外实现仅用于lower_bound()查询的偏序比较逻辑。额外的偏序比较不满足严格全序要求,因此不适合用于find()这类要求精确匹配的接口,除非仅需要找到任意一个匹配项。
另外我提问后才意识到,我想要用自定义比较逻辑实现O(log n)查找本身就有前提:自定义比较逻辑生成的序必须是原严格全序的子序,否则无法保证正确性,这也是为什么没有O(log n)复杂度的find_if()接口,因为通用的find_if()只能做到线性复杂度。
透明运算符函子的技巧非常巧妙,但确实依赖开发者保证自定义比较逻辑的序是原严格全序的子序。
内容的提问来源于stack exchange,提问作者jma

