我有一个数组或N个对(v1, v2)
,其中v1 <= v2
。这些应该代表时间从v1
开始到v2
结束的事件。它们可以相等,那么事件是瞬时的。数组按开始时间v1
排序。
对于给定范围(L, R)
,我想找到L <= v1 <= R or L <= v2 <= R
所在的任何一对。这里的想法是让事件在给定范围内开始,发生或结束。
我的主要问题是效率。该数组可能包含数十万个事件。因此,只能进行遍历所有对的线性搜索。
我读了一些关于kd-tree的信息,但是它的问题是它排除了范围的边界,只会返回L <= v1 <= R AND L <= v2 <= R
。也就是说,只会返回在该范围内实际发生的事件(开始和结束),而我需要开始或结束(或显然是两者)。
我还考虑过保留2个查询表(我使用double作为时间)
std::map<double, Event*> startPoints;
std::map<double, Event*> endPoints;
并在两者中使用std::find
算法并合并结果。
只需寻求建议,这是一个很好的解决方案,还是有更聪明的方法。
编辑:
对此进行重新考虑,这更加复杂。这是预期结果的一个例子
|---Ev1---| |---Ev3---| |---Ev5---|
|---Ev2---| |---Ev4---|
| |
L R
在这里我想获得Ev2(在范围内结束),Ev3(在范围内发生)和Ev4(在范围内开始)
|---Ev1---| |---Ev3---| |---Ev5---|
|---Ev2---| |---Ev4---|
| |
L R
在这里,我想获得当前在范围内运行的Ev3,并在范围内开始运行Ev4
|---Ev1---| |---Ev3---| |---Ev5---|
|---Ev2---| |---Ev4---|
|
LR
在这里,我只想要Ev2,因为它是当前运行的唯一Ev2。
答案 0 :(得分:3)
由于您需要处理三种情况-在给定范围内开始,发生或结束,我们可以将其分为三部分。
v1
位于[L,R]
中。v2
位于[L,R]
中。第三种情况可以表述为v1 <= R and L <= v2
,但是前两种情况部分涵盖了这种情况,因此我们将使用不同的表述来避免冲突:
v1 < L and R < v2
好吧,如果我们可以按v1
对事件数组进行排序,则很容易处理对数形式的第一种情况以及报告的事件时间数。相同的技巧适用于第二种情况。
第三种情况比较棘手。让我们来画:
粉红色区域代表所有间隔L <= R
。红点是一个间隔,绿色区域表示我们要捕获的所有可能的事件。要进行这样的捕获,可以使用k2-tree。
答案 1 :(得分:1)
使用索引方法很好-例如Boost.ICL解决方案。
话虽这么说,您可以轻松地使用std::vector
-即使是未排序的-我认为只要您在100.000甚至1.000.000的范围内,就应该没问题(只要您存储实际值-不存储向量中的指针,因为这样做可能会很慢)-确切的数字当然取决于您的住所。
struct MyEvent {
double v1;//you use double for time
double v2;
};
std::vector<MyEvent> events;
以下是使用1.000.000元素的示例:
http://coliru.stacked-crooked.com/a/9a6d90348f6915e1
搜索将在42毫秒内进行,其中包含一个比较和可选副本,而您的情况可能有些不同,因此具有可比性。
再进一步,您可以通过使用例如来以某种方式并行化搜索来获得更多功能。 std::for_each
。
答案 2 :(得分:-1)
std::map
->查找元素复杂度为O(logn)
如果您的密钥是唯一的,并且没有内存问题,则可以使用std::unordered_map
,其摊销额(O1)。
另外,您不需要创建2张地图。
std::unordered_map<double, std::pair<Event*, Event*>> StartEndPoints;
。
如果您的密钥不是唯一的,则可以使用std::unordered_multimap
,但是如果您的密钥将重复很多次,则查找复杂度可能会变为(On)。
我建议不要将密钥类型作为double
传递。
std::hash<double> hashing.
auto temp = hashing(key). // decltype of temp will be size_t
std::unordered_map<std::size_t, std::pair<Event*, Event*>> StartEndPoints;