【问题标题】:JavaScript runtime complexity of Array functionsArray 函数的 JavaScript 运行时复杂度
【发布时间】:2014-05-02 02:16:57
【问题描述】:

运行时复杂度是否由 JS 标准对常见的 Array 函数(如 pushpopshiftslicesplice)定义?特别是。我有兴趣在随机位置删除和插入条目。如果没有定义复杂性,我可以期待什么,例如在 V8 中?

(这个问题的灵感来自this。另外,this benchmark,发布here,也让我很好奇,但也许这是一些不相关的现象。)

(一个非常相关的问题是here。但是,接受的答案中的一个 cmets 说它现在是错误的。此外,接受的答案没有任何参考标准确实以这种方式定义它。) .

【问题讨论】:

标签: javascript arrays time-complexity


【解决方案1】:

ECMA 规范没有指定边界复杂度,但是,您可以从规范的算法中推导出一个边界复杂度。

pushO(1),但实际上它会在引擎定义的边界处遇到 O(N) 复制成本,因为插槽数组需要被重新分配。这些边界通常是对数的。

popO(1)push 类似的警告,但 O(N) 副本很少遇到,因为它经常被折叠成垃圾集合(例如,复制收集器只能复制数组的已使用部分)。

shift 最坏的情况是 O(N) 但是在特殊情况下,它可以以减慢索引速度为代价实现为 O(1)您的里程可能会有所不同。

sliceO(N),其中 Nend - start。在不显着减慢对两个阵列的写入速度的情况下,这里没有大量的优化机会。

splice 是,最坏的情况,O(N)。有一些数组存储技术可以将 N 除以一个常数,但它们会显着减慢索引速度。如果引擎使用此类技术,您可能会注意到操作异常缓慢,因为它在访问模式更改触发的存储技术之间切换。

你没有提到的是sort。在平均情况下,O(N log N)。但是,根据引擎选择的算法,在某些情况下您可能会得到 O(N^2)。例如,如果引擎使用 QuickSort(即使 InsertionSort 迟到),它有众所周知的 N^2 个案例。这可能是您的应用程序的 DoS 来源。如果这是一个问题,请限制您排序的数组的大小(可能合并子数组)或纾困到 HeapSort。

【讨论】:

  • 这很好,但是如果没有特定于 javascript 的源,很难验证。例如,我知道排序可以是 O(NlogN),但它适用于所有语言吗?不。(另外,过滤器,反向等呢?)
  • 这些很难验证,但您可以相信它们不会比规范中的算法更复杂。至于filterreverse 他们是O(N)
  • concat 呢?
  • 请提及unshift(),我认为这也是O(n)。
  • concat 和 unshift 几乎可以肯定是 O(n)
【解决方案2】:

简单来说

推-> O(1)

流行 -> O(1)

移位 -> O(N)

切片 -> O(N)

拼接 -> O(N)

Here 是关于 JavaScript 中数组时间复杂度的完整解释。

【讨论】:

  • 是的,完全正确! Array.prototype.splice 是一个通用的多功能工具,它的复杂性取决于用它做什么。例如,它可以删除和/或添加最后一个元素(如 push 或 pop),那么复杂度将为 O(1);如果它的执行与 shift / unshift 相同,则其复杂度将为 O(n)。其他“中间”情况也是可能的。
  • 是的,所以我们可以说,在最坏的情况下,它仍然是 O(n)。
【解决方案3】:

顺便说一句,可以使用 RingBuffer (i.o.w CircularQueue / CircularBuffer) 数据结构在 O(1) 中实现 shift / unshift, push / pop 方法。只要循环缓冲区不需要增长,最坏的情况就是 O(1)。有没有人真正衡量过这些操作的性能?知道总比猜测好...

【讨论】:

  • 是的,几个月前我在 jsperf 上尝试过 - shift 和 unshift 的性能确实比 O(1) 更接近 O(n),并且在 n 较大时速度要慢得多。做了这个 Queue 实现以避免 shift 和 unshift gist.github.com/tbjgolden/142f2e0b2c1670812959e3588c4fa8a2
  • @TomGolden - 抱歉,在这里不要太突然,但实现需要几个警告。它从不让任何事情发生。它不仅累积未使用的数组槽,而且还保存对那些未使用的数组槽中的对象的引用。如果将大对象推送到该队列中,它将永远保存它们,如果在使用队列期间添加的总项目的大小会导致内存使用量增加 O(n)。至少,请在使用 dequeue 删除它们后将数组槽设置为未定义。
【解决方案4】:

只有在取数未知的情况下,slice 才是线性的。如果切片数是常数,那么切片是常数,不是线性的。

【讨论】:

  • 任何算法都可以这样说。如果数组中的元素数量是恒定的,那么您可以说对数组进行排序是一个恒定时间操作。由于显而易见的原因,这不是非常有用的信息,因此所有的反对意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 2020-07-25
  • 1970-01-01
  • 2019-06-08
  • 2018-09-20
相关资源
最近更新 更多