【问题标题】:How to find all elements in array included by range efficiently?如何有效地查找范围包含的数组中的所有元素?
【发布时间】:2021-02-16 17:49:15
【问题描述】:

我有一个很大的日期元素数组,数组已经排序了。

[2020.12.31, 2020.12.31, 2021.01.03, 2021.01.04, 2021.01.04, 2021.02.16, 2021.06.16]

还有一些范围:[2021.01.03, 2021.05.15]

我需要找到包含在这个范围内的所有元素:[2021.01.03, 2021.01.04, 2021.01.04, 2021.02.16]

我编写了一个解决方案,但它对大数组效率不高。我认为这可以通过使用二进制搜索来解决,但我不知道如何。

我的代码:

       let i = 0,
        j = eventsId.length - 1,
        startIndex,
        endIndex
      while (!startIndex && !endIndex && i < j) {
        if (!startIndex) {
          if (
            events[eventsId[i]].startDate >= interval.start
          )
            startIndex = i
          else i++
        }
        if (!endIndex) {
          if (events[eventsId[j]].startDate <= interval.end)
            endIndex = j
          else j--
        }
      }
      if (startIndex && endIndex) {
        return eventsId.slice(startIndex, endIndex + 1)
      }

【问题讨论】:

  • 如果您不每次都创建new Dates,您可能会改进它,因为您的日期具有标准化格式的年月日,字符串比较应该也可以工作
  • 一旦你的 endIndex 被设置,你也可能想要打破这个时间
  • @OvidiuDolha 谢谢,我将删除new Date。但我也需要从 O(n) 移动到 O(log n) 我认为
  • 你可以实现某种二进制搜索。您遇到的问题是没有完全匹配,并且可能有多个匹配项。就我个人而言,我会按照你的方式实现它。
  • 你为什么要担心这个的渐近时间复杂度?这些是格式化的日期。您认为它们的阵列会增长到多大?

标签: javascript arrays algorithm


【解决方案1】:

您可以将日期字符串视为字符串并直接与范围值进行比较,而无需转换为Date 对象。

const
    data = ['2020.12.31', '2020.12.31', '2021.01.03', '2021.01.04', '2021.01.04', '2021.02.16', '2021.06.16'],
    range = ['2021.01.03', '2021.05.15'],
    result = data.filter(d => d >= range[0] && d <= range[1]);

console.log(result);

【讨论】:

  • 这个是 O(n)。我认为可以有更有效的方法来解决这个问题。
  • 你有数组中想要的范围的确切日期吗?
【解决方案2】:

我认为您担心不必要的优化。这是一个测试,它需要超过一百万个有序日期,然后在给定范围内找到匹配项。我们使用基于 .filter 的简单函数为您提供的小范围和具有 300,000 多个结果的范围计时。

在我的中档笔记本电脑上,两次通话需要 18 毫秒到 43 毫秒。如果我将它减少到本世纪到目前为止的 8000 左右,它们会在 1 毫秒以下。

您真的需要加快速度吗?你是在一个紧密的循环中做的吗?

const rng = (lo, hi) => [...new Array(hi - lo + 1)] .map ((_, i) => i + lo)

// not exact dates, but fine for demo
const ds = rng(1, 3000)
  .flatMap (y => 
    rng(1, 12) .flatMap (m => rng(1, 30) .map (
      d => `${String(y).padStart(4, '0')}.${String(m).padStart(2, '0')}.${String(d).padStart(2, '0')}`)
    )
  )

const findMatches = (ds) => (start, stop) =>
  ds.filter (d => d >= start && d <= stop)

console .log ('Total dates:', ds .length)
console .time ('calc time')
console .log ('found in [2021.01.03 - 2021.05.15]:', findMatches(ds)('2021.01.03', '2021.05.15') .length)
console .timeEnd ('calc time')

console .time ('calc time 2')
console .log ('found in [1166.01.03 - 2021.05.15]:', findMatches(ds)('1166.01.03', '2021.05.15') .length)
console .timeEnd ('calc time 2')

【讨论】:

    【解决方案3】:

    O(lg N) 解的伪代码

    first = binarysearch(lower)
    last = binarysearch(high+1)
    
    solution = array (first ... last)
    

    binarysearch 应该返回之前每个元素都低于参数的第一个元素

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 2021-03-16
      • 2017-11-01
      相关资源
      最近更新 更多