JavaScript 数组的大 O

IT技术 javascript arrays algorithm big-o time-complexity
2021-01-26 14:23:44

JavaScript 中的数组很容易通过添加和删除项目来修改。它在某种程度上掩盖了大多数语言数组是固定大小的事实,并且需要复杂的操作来调整大小。似乎 JavaScript 使得编写性能不佳的数组代码变得容易。这就引出了一个问题:

在数组性能方面,我可以从 JavaScript 实现中获得什么性能(就大 O 时间复杂度而言)?

我假设所有合理的 JavaScript 实现最多有以下大 O。

  • 访问 - O(1)
  • 附加 - O(n)
  • 前置 - O(n)
  • 插入 - O(n)
  • 删除 - O(n)
  • 交换 - O(1)

JavaScript 允许您使用new Array(length)语法将数组预填充到特定大小(额外问题:以这种方式创建数组是 O(1) 还是 O(n)) 这更像是一个传统数组,如果用作预先调整大小的数组,可以允许 O(1) 追加。如果添加循环缓冲区逻辑,则可以实现 O(1) 前置。如果使用动态扩展数组,则 O(log n) 将是这两者的平均情况。

在某些方面,我能否期望比我的假设更好的性能?我不希望任何规范中概述任何内容,但实际上,所有主要实现都可能在幕后使用优化的数组。是否有动态扩展数组或其他一些性能提升算法在起作用?

聚苯乙烯

我想知道这一点的原因是我正在研究一些排序算法,其中大多数似乎在描述它们的整体大 O 时假设追加和删除是 O(1) 操作。

2个回答

注意:虽然这个答案在 2012 年是正确的,但今天引擎对对象和数组使用非常不同的内部表示。这个答案可能是也可能不是。

与使用数组实现数组的大多数语言相比,Javascript 中的数组是对象,并且值存储在哈希表中,就像常规对象值一样。像这样:

  • 访问 - O(1)
  • 附加 - 摊销 O(1)(有时需要调整哈希表的大小;通常只需要插入)
  • 前置 - O(n) via unshift,因为它需要重新分配所有索引
  • 插入 - 如果该值不存在,则摊销 O(1)。O(n) 如果您想移动现有值(例如,使用splice)。
  • 删除 - 如果您想通过splice.
  • 交换 - O(1)

通常,设置或取消设置字典中的任何键的分摊时间为 O(1),数组也是如此,无论索引是什么。任何需要对现有值重新编号的操作都是 O(n),因为您必须更新所有受影响的值。

另外,是length在 Array 突变上设置,还是get在它上面会得到长度并可能记住它?
2021-03-19 14:23:44
@BenjaminGruenbaum 如果您能对它们的存储方式有所了解,那就太好了。或者给出一些来源。
2021-03-21 14:23:44
值得一提的是,这个答案不再正确。现代引擎不会将数组(或带有索引整数键的对象)存储为哈希表(但就像 C 中的数组一样),除非它们是稀疏的。为了让您开始这里是一个“经典”的基准说明这一点
2021-03-25 14:23:44
不应该是 O(n) 吗?因为所有的索引都需要移动。插入和删除相同(在任意索引处,以及移动/折叠元素)。
2021-04-08 14:23:44
这是由标准定义的还是只是 JS 引擎中的常见实现?V8怎么了?
2021-04-10 14:23:44

保证

任何数组操作都没有指定的时间复杂度保证。数组的执行方式取决于引擎选择的底层数据结构。引擎也可能有不同的表示,并根据某些启发在它们之间切换。初始数组大小可能是也可能不是这样的启发式方法。

现实

例如,V8 使用(截至今天)哈希表数组列表来表示数组。它也有各种不同的对象表示,因此无法比较数组和对象。因此数组访问总是比为O(n)更好,和可能甚至是一样快,一个C ++数组访问。追加是 O(1),除非您达到数据结构的大小并且必须对其进行缩放(即 O(n))。预先准备更糟。如果您执行类似delete array[index](不要!)的操作,删除可能会更糟,因为这可能会迫使引擎更改其表示形式。

建议

将数组用于数值数据结构。这就是他们的目的。这就是引擎将优化它们的目的。避免使用稀疏数组(或者,如果必须的话,期望性能更差)。避免使用混合数据类型的数组(因为这会使内部表示更加复杂)。

如果您真的想针对某个引擎(和版本)进行优化,请查看其源代码以获得绝对答案。

等一下,我们可以有混合数据类型的数组吗?JavaScript 太酷了!
2021-03-23 14:23:44
@Anurag 完全正确,但在 99% 的情况下您不需要此功能
2021-03-29 14:23:44
@Jonas-Wilms,你能详细说明一下unless you reach the size of the datastructure and it has to be scaled吗?我们通常不会在 JS 中创建数组时设置数组长度,这是否意味着附加到任何以常规方式创建的数组(如let a = [1,2,3])需要数据结构缩放?
2021-04-05 14:23:44
@grereeenn 我猜不是,引擎可能会直接分配一个更大的数组列表
2021-04-13 14:23:44