有效地找到落在一定数量范围内的物体

时间:2012-10-25 07:16:58

标签: javascript algorithm loops time

这是我的基本问题:我给了currentTime。例如,750秒。我还有一个包含1000到2000个对象的数组,每个对象都有startTimeendTime_id属性。鉴于currentTime,我需要找到startTimeendTime属于该范围的对象 - 例如startTime : 740endTime : 755

在Javascript中执行此操作的最有效方法是什么?

对于初学者来说,我只是做了这样的事情:

var arrayLength = array.length; 
var x = 0;
while (x < arrayLength) {
 if (currentTime >= array[x].startTime && currentTime <= array[x].endTime) {
  // then I've found my object
 }
x++;
};

但我怀疑循环不是这里的最佳选择。有什么建议吗?

编辑:为清楚起见,currentTime必须属于startTimeendTime

我的解决方案:我的数据结构为我提供了一些好处,可以让我简化一些事情。我已按照建议完成了基本二进制搜索,因为数组已经按startTime排序。我还没有完全测试过这个东西的速度,但我怀疑它的速度要快一些,特别是对于更大的阵列。

var binarySearch = function(array, currentTime) {

  var low = 0;
  var high = array.length - 1;
  var i; 

  while (low <= high) {
    i = Math.floor((low + high) / 2);

    if (array[i].startTime <= currentTime) {

      if (array[i].endTime >= currentTime ){
        // this is the one
        return array[i]._id; 

      } else {
        low = i + 1;
      }
    }

    else {
      high = i - 1;
    }
  } 

  return null;
}

6 个答案:

答案 0 :(得分:5)

解决此问题的最佳方法取决于您必须调用搜索功能的次数。

如果您只调用几次,请说m次,进行线性搜索。调用此函数的总体复杂度为O(mn)

如果您多次调用您的函数,并且许多我的意思超过log(n)次,您应该:

  • O(nlogn)之前按startTime对数组进行排序,如果您有多个具有相等值endTime
  • 的项目,请按startTime排序
  • 执行binary search以查找startTime <= x的元素范围。这意味着执行两个二进制搜索:一个用于范围的start,另一个用于范围的end。这是在O(logn)
  • 中完成的
  • [start, end]内进行线性搜索。您必须执行线性搜索,因为startTimes的顺序不会告诉您endTimes。这可以是O(1)O(n)之间的任意位置,具体取决于细分的分布和x的值。

平均情况: O(nlogn)用于初始化,O(logn)用于每次搜索。

最坏情况:包含许多相等段的数组,或具有公共间隔的段,并在此间隔内搜索。在这种情况下,您将O(nlogn)进行初始化,O(n + logn) = O(n)进行搜索。

答案 1 :(得分:2)

听起来像是binary search的问题。

答案 2 :(得分:2)

假设您的搜索数组是长寿命且相对恒定的,第一次迭代将按开始时间对所有数组元素进行排序(或者如果您不想要,则创建指向数组元素的已排序开始时间的索引他们排序)。

然后你可以有效地(使用二进制印章)折扣,开始太晚了。对其他人的顺序搜索会更快。

即使更多速度,也要为开始和结束时间维护单独的排序索引。然后按照前面提到的相同操作扔掉那些开始得太晚的操作。

然后,对剩下的那些,使用结束时间索引扔掉那些过早结束的那些,你剩下的就是你的候选名单。

但是,确保这实际上是需要的。两千个元素看起来不是很大,所以你应该对当前方法进行计时,并且只有在确实存在问题时才尝试优化。

答案 3 :(得分:1)

根据给出的信息,无法确定什么是最佳解决方案。如果数组未排序,则循环是单个查询的最佳方式。沿阵列的单次扫描只需要O(N)(其中N是数组的长度),而对它进行排序然后进行二进制搜索将需要O(N log(N)+ log(N)),因此它在这种情况下会花更多的时间。

如果在同一个大型阵列上有大量不同的查询,分析会有很大的不同。如果在同一个阵列上有大约N个查询,则排序实际上可能会提高性能,因为每个Query将采用O(log(N))。因此,对于N个查询,它将需要O(N log(N))(现在剩余的log(N)被丢弃)而未排序的搜索也将采用明显更大的O(N ^ 2)。当排序开始产生影响时,也取决于数组的大小。

当您经常更新阵列时,情况也会有所不同。更新未排序的数组可以在O(1)分期中完成,而更新排序的数组则需要O(N)。因此,如果你有相当频繁的更新,排序可能会受到伤害。

范围查询还有一些非常有效的数据结构,但是如果它们有意义,它又取决于实际使用情况。

答案 4 :(得分:1)

如果数组没有排序,那么你的数组是正确的。

不要陷入思考首先对数组进行排序,然后应用搜索的陷阱。

使用您尝试过的代码,您的复杂性为 O(n),其中 n 是元素的数量。

如果您首先对数组进行排序,首先会在average case中陷入 O(n log(n))(与Sorting algorithm比较)的复杂性。

然后你必须应用二进制搜索,它的执行平均复杂度为 O(log_ 2(n) - 1)

所以,你最终会花费,在一般情况下

O(n log(n) + (log_2(n) - 1))

而不只是 O(n)

答案 5 :(得分:1)

interval tree是一种数据结构,如果总共有n个区间,则允许在O(lg n)时间内(平均和最差情况下)回答此类查询。构造数据结构的预处理时间是O(n lg n);空间是O(n)。 augmented区间树的插入和删除时间为O(lg n)。如果m个间隔覆盖一个点,则回答所有间隔查询的时间是O(m + lg n)。 Wikipedia描述了几种间隔树;例如,居中间隔树是第三树,每个节点存储:

  

•中心点
  •指向另一个节点的指针,该节点包含完全位于中心点左侧的所有间隔   •指向另一个节点的指针,该节点包含完全位于中心点右侧的所有间隔   •所有与中心点重叠的间隔按其起点分类   •所有间隔重叠中心点按其结束点排序

注意,对于找到一个覆盖点的间隔的平均和最坏情况查询,间隔树具有O(lg n)复杂度。以前的答案具有相同的O(n)最坏情况查询性能。之前的几个答案声称它们的平均时间为O(lg n)。但他们都没有提供证据;相反,他们只断言平均表现为O(lg n)。之前答案的主要特征是使用二进制搜索开始时间。然后有人说使用线性搜索,而其他人说使用二进制搜索,结束时间,但没有明确后一个搜索结束的间隔集。他们声称平均表现为O(lg n),但这仅仅是一厢情愿的想法。正如维基百科文章 Naive Approach 标题下所指出的那样,

  

一种天真的方法可能是构建两个并行树,一个按起始点排序,另一个按每个间隔的结束点排序。这允许在O(log n)时间内丢弃每棵树的一半,但结果必须合并,需要O(n)时间。这给了我们O(n + log n)= O(n)的查询,这不比蛮力好。