用于查找基于时间的事件的最佳Javascript算法
Optimal Javascript algorithm for finding time-based events
我在Javascript中有一个对象数组,其中包含以毫秒为单位的事件开始和结束时刻。我们的代码库目前使用一种简单的搜索算法,该算法在数组中迭代,直到我们找到包含某个时刻的事件:
// time contains the moment of time we're looking to find an event for
profile.tempbasaltreatments.forEach( function eachTreatment (t) {
if (time <= t.endmills && time >= t.mills) {
return t;
}
});
这与较大数据集的性能不符。什么是一个好的算法/数据模型,可以有效地遍历对象数组,找到封装某个时刻的事件?您可以假设,在事件重叠的情况下,第一次匹配总是足够的。
我建议这些预处理步骤(在搜索之前):
- 如果出于其他目的需要,可以选择复制原始阵列
- 按事件开始时间对数组进行排序。如果可能的话,这实际上应该由数据库来完成,数据库可以为此维护索引
- 从数组中删除结束时间早于上一个事件结束时间的事件。当某个时间与此删除的事件匹配时,它也可以与上一个事件匹配。由于匹配任何事件都足够好,我们可以删除此事件
然后搜索将是二进制的,如下所示:
- 将搜索范围设置为整个数组。范围表示为数组中的起始索引和结束索引(而不是两次)
- 取该范围的中间元素
- 如果此事件与给定时间匹配,则成功退出
- 如果此事件的开始时间大于给定时间,则从步骤2开始,用所选元素之后的一半范围重复
- 否则,取范围的另一半(在选定元素之前),从2开始重复
- 如果范围中没有更多事件,请停止重复,并失败退出
预处理应该只进行一次,如果您仍然需要排序,则时间复杂度为O(n log n),否则为O(n)。完成后,您可以在O(logn)时间内重复查找事件。
以下是上面的一些JavaScript代码:
// Create a copy of the original array and sort it by event start date
var events = profile.tempbasaltreatments.slice(0).sort(function (a, b) {
return a.mills - b.mills;
});
// Remove events that fall completely within the limits of another event.
// They are not needed to find an event for a given time.
for (var i = 0; i < events.length-1;) {
if (i && events[i].endmills < events[i-1].endmills) {
events.splice(i, 1);
} else {
i++;
};
}
// Now also the remaining events' end dates are sorted
// function for fast search in events:
function findEvent(events, time) {
// Binary search for event
var first = 0, last = events.length - 1;
while (first <= last) {
var i = first + Math.floor((last - first) / 2);
var t = events[i];
if (time >= t.mills && time <= t.endmills) return t;
if (time < t.mills) {
last = i - 1;
} else { // time > t.endmills
first = i + 1;
}
}
// returns undefined
}
// Example call: find a currently running event:
var foundEvent = findEvent(events, new Date().getTime());
附录
以下是最后一个预处理步骤中的过滤过程。首先是事件在开始时间排序后的排序时间表:
a: ---------------
b: -------
c: ------------------
d: --
e: --
f: -----
可以消除的事件是b:
a: ---------------
c: ------------------
d: --
e: --
f: -----
则d:
a: ---------------
c: ------------------
e: --
f: -----
则e:
a: ---------------
c: ------------------
f: -----
和f:
a: ---------------
c: ------------------
很明显,在过滤之前,总覆盖期与原始覆盖期相同。
如果事件按开始时间、"中间时间"或结束时间排序,则可以使用二进制搜索来查找附近的事件,然后进行局部线性搜索,以找到包含时间戳的事件(局部搜索方向取决于排序顺序:如果事件是按开始时间排序的,则需要从最近的开始时间开始,按开始时间递减的方向进行搜索)。
这种方法的主要问题是,当没有最长事件持续时间时,它会很慢,因为这意味着除了到达列表的末尾之外,本地搜索没有停止标准。
更好的方法可能是将事件存储在适合高效访问的数据结构中,例如间隔树。
相关文章:
- 什么'是在HTML5画布中创建关键事件的最佳方式
- 在Nodejs中堆叠异步回调事件的最佳方式
- addEvent()传奇事件之后的当前最佳实践是什么
- 主干.js事件处理程序命名的最佳做法
- 在 Web 应用中处理事件跟踪的最佳(高性能)方法
- 将 react-redux 与基于事件的第三方库一起使用的最佳方法是什么?
- 用于查找基于时间的事件的最佳Javascript算法
- 通知全局事件组件的最佳方式
- 处理多次单击事件的最佳方式
- 在大型系统中附加事件处理程序的最佳做法
- 为输入字段(而不是表单的一部分)设置默认按钮(或在 javascript 中触发其事件)的最佳方法
- 当文档中的单个元素准备就绪时触发事件的最佳方法
- Javascript:注册事件处理程序的最佳位置
- 不再需要视图后取消委派事件的最佳方法
- React,在组件事件上呈现消息的最佳方法
- 这真的是从事件回调中获取 Rx.Observable 的最佳方式吗?
- 使用jsduck记录事件处理程序的最佳方式是什么
- 什么'是在AngularJS中处理应用程序状态和事件广播的最佳实践
- 从中停用单击事件的最佳方式是什么
- 跟踪javascript中文本框控件上触发的退格事件位置的最佳方法