我有大量(~10 9 )事件,其特点是开始和结束时间。有一段时间,我想知道当时有多少事件正在进行中。
在这种情况下,哪种数据结构会有所帮助?我需要快速的操作是:
{start: 100000 milliseconds, end: 100010 milliseconds}
。更新:有人在上面放了一个计算几何标志,所以我想我应该用计算几何来重新表述。我有一组1维间隔,我想计算这些间隔中有多少与给定点相交。插入新的间隔必须很快。
答案 0 :(得分:8)
您正在寻找interval tree。
O(n log n)
,其中n
是间隔数O(m+log n)
,其中m
是查询结果的数量,n
是间隔数O(n)
答案 1 :(得分:3)
只需添加其他答案,根据时间长度和所需的粒度,您可以只使用一组计数器。例如,如果时间长度为24小时且所需粒度为1毫秒,则阵列中将有86,400,000个单元。每个单元一个4字节int(足以容纳10 ^ 9),这将少于700 MB的RAM,而基于树的解决方案至少需要(8 + 8 + 4 + 4)* 10 ^ 9 = 24个RAM用于两个指针加上每个树节点两个整数(因为32位可寻址存储器不足,每个指针需要64位)。您可以使用swap,但这会大大减慢一些查询。
如果您只关心过去24小时的数据,也可以使用此解决方案,例如,将数组用作循环缓冲区。除了时间和粒度的限制外,另一个缺点是间隔的插入时间与间隔的长度成正比,因此如果间隔长度无限制,则可能会遇到麻烦。另一方面,查询是单个数组查找。
答案 2 :(得分:2)
(通过tskuzzy和Snowball扩展答案)
平衡二进制搜索树是有意义的,除了数据集的内存要求过高。除非你可以使用库,否则B-tree会更有效,尽管更复杂。
保留两棵树,其中一棵是开始时间,另一棵是结束时间。要插入事件,请将开始时间树的开始时间和结束时间树的结束时间相加。要在时间T查询活动事件的数量,搜索开始时间树以查找有多少开始时间小于T,并搜索结束时间树以查找有多少结束时间小于T.减去从开始次数开始的结束次数,也就是活动事件的数量。
插入和查询都应该花费O(log N)时间。
一些评论:
您提出问题的方式,您只关心活动事件的数量,而不是哪些事件处于活动状态。这意味着您无需跟踪哪个开始时间与哪个结束时间一致!这也可以更容易地避免先前答案引用的查询中的“+ M”术语。
请注意查询的确切语义。特别是,如果事件在时间T开始,则事件在时间T计为有效吗?如果它在时间T结束?这些问题的答案会影响您是否使用<或者< =在某些地方。
不使用“set”数据结构,因为您几乎肯定希望允许和计算重复项。也就是说,多个事件可能同时开始和/或结束。一组通常会忽略重复。你要找的是一个“multiset”(有时也称为“bag”)。
许多二叉搜索树不支持开箱即用的“元素数量< T”查询。但是通过在每个节点上存储大小来添加此功能很容易。
答案 3 :(得分:0)
假设我们有一个带有 N 元素的有序集(例如,balanced binary search tree或skip list)数据结构。此外,假设排序集具有 O(log N)搜索时间, O(log N)插入时间,以及 O(N)空间使用(这些是合理的假设,例如见red-black tree)。
一种可能性是有两个有序集合bystart
和byend
,分别按事件的开始和结束时间排序。
要查找时间t
正在进行的事件数,请byend
询问结束时间大于t
的第一个时间间隔: O(log N )搜索操作。调用此间隔left
的开始时间。现在,请bystart
询问开始时间大于或等于left
且小于t
的时间间隔数。这是 O(log N + M),其中 M 是这样的间隔的数量。因此,搜索的总时间为 O(log N + M)。
对于有序集合,插入是 O(log N),我们必须为每个有序集合执行一次。这使得插入操作的总时间 O(log N)。
从头开始构建此结构只包含 N 插入操作,因此构建的总时间为 O(N log N)。
每个有序集的空间使用量为 O(N),因此总空间使用量为 O(N)。
要点: