【问题标题】:what is the time complexity of array.splice(...array.splice())array.splice(...array.splice()) 的时间复杂度是多少
【发布时间】:2020-05-29 16:55:43
【问题描述】:

我正在解决一个旋转数组的 leetcode 问题。我通过使用两个 for 循环来解决 O(N) 问题,但是我的运行时间比这个解决方案低。

我的解决方案:运行时间 80 毫秒

var rotate = function(nums, k) {

    let a = Array.from({length:nums.length})

    for (let i = 0; i < nums.length; i++) {
      a[(i + k) % nums.length] = nums[i];
    }
    for (let i = 0; i < nums.length; i++) {
      nums[i] = a[i];
    }


};

其他解决方案:运行时间 52ms

var rotate = function(nums, k) {
      return nums.splice(0,0,...nums.splice(nums.length - k))
};

这怎么可能。据我所知,拼接是 O(N) 并且在这个解决方案中拼接也被再次用于拼接。所以这个解决方案应该是 O(N2) 而我的解决方案应该比这个更快?

【问题讨论】:

  • 你对 O(n2) 有什么推断? Javascript首先确定参数的结果是O(n),然后对这个结果执行O(n)操作,所以你仍然有O(n)的复杂性。无论如何,为什么要估计您是否可以分析这两种解决方案并确切知道执行所需的资源。

标签: javascript arrays splice


【解决方案1】:

Array#splice 是 O(n),因为每次调用它可以向上移动到每个数组元素。见:What's the time complexity of array.splice in Google Chrome?

在确定一个算法的time complexity时,只要每个O(n)操作是串联的并且O(n)操作的数量是一个常数因子(这里就是这种情况),它仍然是O( n)。 O(n2) 是嵌套循环的结果;也就是说,对于每个n,我们执行另一个 O(n) 操作。

此代码仅使用对 Array#splice 的两次连续调用以及 O(n) 扩展,总共 O(3n) 相当于 O(n)。

“我的运行时间比较慢” -- 时间复杂度和运行时间是完全不同的东西。大部分运行时间取决于实现细节、大 O 忽略的固定间接费用(常量因素)、LC 的测试用例有多大、测试用例的组成、LC 的测试用例测量不准确等。根据自己的情况使用基准控制非常大的随机输入更加确定。两个 O(n) 代码很少表现出相同的性能特征——时间复杂度只与算法的增长率有关。

至于这个特殊的比较,请记住内置函数是高度优化的。我假设它在 NodeJS (V8) 中运行,所以很多 builtins are written in C++ (or Torque) (并且 V8 在这方面并不是唯一的)。

在较低级别本地实现内置函数意味着内存可能是compactregular,因此缓存访问受益于locality、更少的指针追逐、stack allocation 的机会等for 循环在高级代码中更难优化。它们涉及索引变量、执行比较、分支等等,所有这些都需要以一种可能不是最佳的方式有效地转换为字节码。如果内置实现是在 JS 中,设计人员已经做了很多工作以使其尽可能高效。

Array#splice 的情况下,它是written in Torque,即translated to efficient assembly

在您的代码中,let a = Array.from({length:nums.length})。导致两个对象的分配开销。

简而言之:公平地进行基准测试,不要将复杂性和速度混为一谈,并尽可能使用内置函数(使用分析器来确定罕见的情况,而不是)。

【讨论】:

  • 感谢您的全面回答,我仍在努力了解每个部分
猜你喜欢
  • 2011-07-07
  • 2021-05-02
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 2020-07-13
  • 2018-11-24
  • 2014-05-27
  • 1970-01-01
相关资源
最近更新 更多