Array 函数的 JavaScript 运行时复杂性

IT技术 javascript arrays time-complexity
2021-02-06 13:39:13

由JS标准共同定义的运行复杂Array的功能,如pushpopshiftslicesplice特别是 我有兴趣在随机位置删除和插入条目。如果没有定义复杂性,我可以期待什么,例如在 V8 中?

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

这里有一个非常相关的问题。但是,对已接受答案的评论之一说它现在是错误的。此外,已接受的答案没有任何参考标准真正以这种方式定义它。)。

4个回答

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

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

popO(1)与类似的警告pushO(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。

请提及unshift(),我认为这也是 O(n)。
2021-03-14 13:39:13
值得一提的是,即使对于稀疏数组,这也是正确的N 中包含空元素。
2021-03-14 13:39:13
这些很难验证,但你可以相信它们并不比规范中的算法更复杂。至于filterreverse它们O(N)
2021-04-07 13:39:13
这很好,但没有特定于 javascript 的源代码,很难验证。例如,我知道排序可以是 O(NlogN),但它是否适用于每种语言?不。(另外,过滤器、反向等呢?)
2021-04-08 13:39:13
怎么样concat
2021-04-12 13:39:13

用非常简单的话

推 -> O(1)

弹出 -> O(1)

移位 -> O(N)

切片 -> O(N)

拼接 -> O(N)

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

是的,所以我们可以说,在最坏的情况下,它仍然是 O(n)。
2021-03-18 13:39:13
对,就是这样!Array.prototype.splice是一个通用的多功能工具,它的复杂程度取决于用它做什么。例如,它可以删除和/或添加最后一个元素(如推送或弹出),那么复杂度将为 O(1);如果它的性能与 shift / unshift 相同,则其复杂度将为 O(n)。其他“中间”情况也是可能的。
2021-04-07 13:39:13

顺便提一下,可以使用 RingBuffer (iow CircularQueue / CircularBuffer) 数据结构在 O(1) 中实现shift/ unshift, push/pop方法。当循环缓冲区不需要增长时,最坏的情况是 O(1)。有没有人实际测量过这些操作的性能?知道总是比猜测更好......

@TomGolden - 抱歉,在这里不要太突然,但该实施需要一些注意事项。不放过任何东西。它不仅累积未使用的数组槽,而且还保存对那些未使用的数组槽中的对象的引用。如果将大对象推入该队列,它将永远保留它们,如果在队列使用期间添加的总项目的大小增加,则会导致内存使用量增加 O(n)。至少,请在使用 dequeue 删除它们后将数组插槽设置为 undefined。
2021-03-24 13:39:13
是的,我几个月前在 jsperf 上尝试过 - shift 和 unshift 确实比 O(1) 更接近 O(n),并且在大 n 时慢得多。使这个队列实现避免移位和取消移位gist.github.com/tbjgolden/142f2e0b2c1670812959e3588c4fa8a2
2021-04-01 13:39:13

slice 仅在所取数字未知时才为线性。如果切片数是常数,则切片是常数,不是线性的。

任何算法都可以这样说。如果数组中的元素数量是常数,那么您可以说对数组进行排序是一个常数时间操作。出于显而易见的原因,这不是非常有用的信息,因此所有反对票。
2021-03-24 13:39:13