【发布时间】:2021-11-14 10:27:55
【问题描述】:
我有一张 (C++ 14) 地图
using MyTuple = tuple<A, B, C>;
map<MyTuple, MyData>
其中MyTuple 具有明显的operator<(),它首先比较 A,然后是 B,然后是 C。
我想做一个O(ln N) 搜索匹配某个常量(a, b) 的键。显然,如果有的话,它们将是连续的。所以基本上我想要
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.first is still an (a,b) */) {
// Do something with iter.
// Increment iter.
}
但是map::lower_bound_if 没有函数,而map::find 和map::lower_bound 却是一个完整的MyTuple。我可以(粗暴地)找到一个比我的数据中的任何值都低的 C 值,尽管随着时间的推移这很脆弱。我可以自己编写函数,尽管它可能取决于我当前的本地实现 std::map。
我错过了一个明显的解决方案吗?
更新
我接受的解决方案是在比较函数(透明运算符函子)中使用部分关键数学运算,这是自 C++14 以来的新功能,并且花费的时间比我愿意承认的要长。 This article 是一个很好的心理破冰者,this SO question 在我理解后也很好。
基本见解是考虑一组对象,这些对象按对象的某个键排序。例如,一组员工的排序键为employee.id。我们希望能够搜索员工或整数 id。因此,我们创建了一个 bool operator()() 结构,其中包含我们可能想要比较的各种方式。然后重载决议完成剩下的工作。
在我的例子中,这意味着我可以提供使我的代码工作所需的严格的总排序,而且还提供一个只强制部分排序的比较器,我只用于lower_bound() 查找。因为额外的比较器不提供严格的总排序,所以它不适合,例如,find(),除非我们的意思是“查找其中之一”。
顺便说一句,我在提出我的问题后意识到这在某种程度上有点愚蠢。我想要一个O(ln n) 查找但想使用不同的排序功能。这不能保证有效:它取决于我提供的排序函数,它确实提供了一个排序,它是严格总排序的子排序。如果我不这样做,它显然会失败。这就是为什么没有O(ln n) 函数find_if() 的原因,因为它只能是线性的。
确实,透明运算符仿函数的技术很聪明,但确实取决于程序员提供的不比子排序差。
【问题讨论】:
-
一个
map的tuple<A, B>到map的C到MyData? -
我认为您可以将std::lower_bound 与地图一起使用。有了它,您可以提供自定义比较器。
-
您确定要这样做吗?标准库映射are slow。