我正在寻找一种有效的数据结构,这将允许我提示事件......也就是说,我将拥有一个应用程序,在执行的任何时候,有可能会引发一个事件对于未来的执行点......如:
所以我想拥有一个数据结构,我可以在任何时间放入任何时间以及我可以获得的地方(通过这样做)删除所有应有的事件......还有,是的,如果我能够从数据结构中删除一个事件(因为它已被取消)...虽然不太重要,因为我可以简单地将其标记为已取消......
我的第一个想法是,可能要做某种树,但我想删除 - 因事件部分需要大量的再平衡......
我正在考虑简单地使用一个int哈希,将时间戳映射到null或在那个时间点发生的事件堆栈......我认为在场景中,有很多事件(可能每秒多次 - 哪个是我打算合作的),毕竟这实际上并不是一个坏主意......
所以我很想听听你的意见......:)
修改
感谢
back2dos
答案 0 :(得分:10)
我相信你正在寻找一个Priority Queue,其中包含事件发生时的时间戳作为优先级(好的,更低的时间戳将是更高的优先级)
对您的使用案例稍作澄清:
...我可以把任何事件放进去 将来的任何时候......
使用insertWithPriority插入优先级队列,使用事件发生时的时间戳。这将是O(lgN)
......我可以得到的地方(通过这样做 所以)删除所有到期事件......
你反复调用getTop(获取时间戳最短的事件)收集所有感兴趣的元素。
......如果我是的话,还会加分 能够从中移除一个事件 数据结构(因为它是 取消)......不太重要 但是,因为我可以简单地将其标记为 取消..
这是可能的,但由于重新平衡将是O(lgN)。
答案 1 :(得分:3)
N有多大?您多久需要插入一次&删除项目,与其他一切相比?如果这超过总执行时间的10%,并且如果N通常超过100(比如说),那么可能是时候关注big-O了。我已经看到使用花哨的容器算法实现优先级队列的程序,分配迭代器,哈希映射,堆等,并花费所有时间来创建和释放抽象对象,其中中间队列长度就像三
ADDED:好的,因为N~10 ^ 6,频率是~100hz,你可能想要某种二进制树或堆的O(log(N))插入/移除时间。如果你愿意将1%的CPU时间用于此,那就是10 ^ 6微秒* 1%/ 100 = 10 ^ 2微秒/操作。这应该不是很困难,因为如果典型的搜索深度是20,每次比较大约50ns,那么进行搜索大约需要1微秒。只要确保保持简单,不要将所有内容都包含在抽象数据类型中。您不必担心分配/释放树节点所花费的时间,因为每个操作只分配/释放一个节点。不需要经常进行重新平衡,例如可能仅在每1000次操作之后。如果您可以批量收集插入,然后以随机顺序插入它们,这可能会阻止树太不平衡。如果您的许多事件是同步的,您可以在时间码中添加少量噪音,以防止树的某些部分变得更像线性列表。
答案 2 :(得分:2)
好的,我要感谢大家的答案 - 非常有趣和乐于助人。 :)
PriorityQueue绝对是我正在寻找的合适名词 - 谢谢你。 现在一切都是关于实施的。
以下是我的想法:
设N是队列的大小,M是处理时每个时间戳的平均事件量(可以说是“并发”事件)(事件密度不均匀分布,“远期未来” “变得更加稀疏,但随着时间的推移,这个时间区域变得更加密集(实际上,我认为最大密度将在未来的4到12小时内某处))。我正在寻找一个可扩展的解决方案,对于相当大的M表现良好。目标是在一秒钟内真正处理那些M到期事件,所以我想花最少的时间来寻找它们。
实际上,我想讨论阵列方法。 O(M + T)不好。一点也不。但是我把它放在脑子里,这就是我想出的:
第一个想法:懒惰
O(T)可能会受到任意因素的影响,引入一些懒惰,但最终它会保持O(T)。但那有多糟糕?我们有T = 2419200,这是28天。然后,每天我会清理一次(最好是在预期低负荷时)。那浪费不到阵列的5%。在我的目标平台上,复制操作在一个相当老的2GHz核心上需要31毫秒,所以它毕竟不是一个坏主意。
第二个想法:大块
在思考了一下后,我想到了这个解决方案:间隔散列,间隔(即给定时间范围)又是一个事件列表数组。间隔都是相同的大小,最好是简单的,如几天或几小时。
对于插入,我通过哈希查找正确的间隔(如果不存在则创建),并在间隔中查找正确的事件列表(如果不存在则再次创建)然后只插入它,即O( 1)。
为了处理,我只需要处理当前到期事件列表,然后处理它,只需获取当前间隔并处理到期事件。数组保持恒定长度,因此我们处于O(M)(这是处理M元素时最好的)。一旦当前间隔被完全处理(因此如果间隔现在代表“过去”),我只需将其处理为O(1)。我可以保留对当前间隔的额外参考,无需查找,但我想这并没有提供任何显着的改进。
在我看来,第二次优化确实是最好的解决方案,因为它快速且无约束。为间隔选择合适的大小允许优化内存开销与散列查找开销。我不知道,我是否应该担心哈希查找时间。对于高M,它应该不重要,是吗?因此,我选择间隔大小为1,这使我回到接近3号。
我真的非常感谢你的任何投入。
答案 3 :(得分:1)
如果您的事件有一个明确定义的上限(例如,将来没有事件发生在2天以后),您可以简单地将一个数组从“开始时间”开始的秒数索引。 数组的值是该偏移量处的事件列表。
列表或删除非常有效 - 只需找到您希望列出或切断的时间的偏移量,然后获取或重新初始化该偏移量后索引所指向的数组。
如果您的事件可以无限延伸到未来,那么您自己想要使用从偏移到事件列表的散列图是最好的,有一个扭曲 - 有一个排序列表(但是你希望实现它)已知的偏移量,这样你就可以获得非常有效的查找(例如,你不必遍历地图上的每个关键点)。
您不需要从已知偏移列表中删除任何内容,因此不需要重新平衡 - 只需从hashmap指向的数组中删除。
此外,您的问题似乎还不清楚是否需要知道“t” - 事件发生的时间。如果您需要了解它,请将其存储为活动的一部分。但是事件发生时的引用应该都是绝对的一些起点(如果它是一个无限范围的散列图,你可以使用epoch秒,如果事件有像我列出的第一个数组解决方案那样的边界,你应该而是使用“自范围开始以来的秒数” - 例如从昨天开始。