【问题标题】:What is the Time and Space Complexity of the 3Sum problem with the following algorithm?以下算法的 3Sum 问题的时间和空间复杂度是多少?
【发布时间】:2019-12-29 15:32:03
【问题描述】:

有一个问题要求返回数组中所有唯一的三元组元素,它们加起来为零(交换三元组中两个元素的位置不算作唯一)。

我想出了以下代码:

function threeSum(nums) {
  nums.sort((a, b) => a - b);
  const result = [];
  for (let i = 0; i < nums.length; i++) {
    // skipping duplicates
    if (i !== 0 && nums[i] === nums[i - 1]) continue;
    let left = i + 1;
    let right = nums.length - 1;
    while (left < right) {
      const s = nums[i] + nums[left] + nums[right];
      // too small; move to the right
      if (s < 0) left++;
      // too big; move to the left
      else if (s > 0) right--;
      // bingo
      else {
        result.push([nums[i], nums[left], nums[right]]);
        //skipping duplicates
        while (left + 1 < right && nums[left] === nums[left + 1]) left++;
        while (right - 1 > left && nums[right] === nums[right - 1]) right--;
        left++;
        right--;
      }
    }
  }
  return result;
};
// expected output: [[-4,-2,6],[-4,0,4],[-4,1,3],[-4,2,2],[-2,-2,4],[-2,0,2]]
console.log(threeSum([-4,-2,-2,-2,0,1,2,2,2,3,3,4,4,6,6]))

我认为时间复杂度是O(n^2)。开头有一个排序,我们假设是 O(n log n) 并且嵌套循环大约工作 (n^2)/2 次,转换为 O(n^2)。所以,最后,我们只剩下 O(n log n + n^2) 并且由于 n log n 的程度较小,它被删除了,剩下的就是O(n^2).

我不太确定空间复杂度,但直觉上我猜这是一个 O(n)

能否请您纠正/确认我对时间和空间复杂度的推理/猜测?

【问题讨论】:

    标签: javascript algorithm time-complexity computer-science space-complexity


    【解决方案1】:

    这看起来像是在二次时间内求解 3SUM 的标准方法。但是,我不同意关于空间复杂度的其他答案,并认为它是二次的,因为可以有许多不同的三元组二次求和为 0。


    考虑以下示例:

    [1, -1, 2, -2, ..., n-1, 1-n, n, -n],其中n 是偶数。

    在这种特殊情况下,有 n²/4 - n/2 不同的三元组总和为 0(请参阅下面的此结果的推导)。这是数组大小的二次方(数组长度为2*n 元素)。因为您存储所有这些解决方案,这意味着您需要二次内存,而线性 O(n) 不会削减它。

    因此,最坏情况的空间复杂度也是二次的(很容易证明,不能有多个不同的三元组总和为 0 的二次方以上)。


    推导结果:

    对于这个序列中的任何正数a,我们可以选择b = kc = -(a+k)得到一个三元组,其中a是绝对值中最小的元素,前提是k &gt; aa+k &lt;= na &lt; k &lt;= n-a。这给了我们n-2*ak 的选择(我们从不计算两次,因为b 总是积极的,c 总是消极的)。

    对所有可能的a 求和,我们总共得到: sum((n-2*a) for a in 1...n/2) = n²/4 - n/2 = Ω(n²).

    【讨论】:

    • 感谢@tassle,这很有启发性。我知道你要去哪里。刚刚做了一个小测试,结果的长度是输入长度的两倍多。但是你的答案有点神秘:)你能举一个具体的例子吗?感谢您的帮助。
    • 它的要点是,对于我考虑的特定示例(即,[1,-1,2,-2,...,n,-n] 对于任何你想要的偶数 n ),有 n²/4 - n/2 不同的三元组总和为 0。这是数组大小的二次方(数组的长度为 2*n 个元素)。因为您存储所有这些解决方案,这意味着您需要二次内存,而线性 O(n) 不会削减它。
    • 至于具体性,我不知道如何才能比我给出的示例更具体:/
    • 不,那很好:) 谢谢。可以将示例添加到您对一些未来迷路的幽灵的回答中。
    • 这样更好? :)
    【解决方案2】:

    是的,时间复杂度为 O(n^2),其中 n = nums.length,您对此本身的解释就足够了。

    空间复杂度 O(n) 也是正确的,因为 sort() 方法中使用的合并排序算法具有这种空间复杂度。空间复杂度是指除了给定的问题特定变量之外,您在代码中编写的额外元素/变量空间。对于除sort()方法之外的代码,有两个变量'left'和'right'以及一个数组结果,所以这个循环的空间复杂度为O(n+2),然后在内部while循环中对应它们也有一个变量's',那么请注意在每次迭代中都有两种可能性:-

    1. 它是同一个容器(即内存位置),其内容(即变量的值)会发生变化,因此总体而言它只有 3 个容器已被使用,其空间复杂度为 O(constant) 或简单地 O(1)李>
    2. 每次为变量分配不同的容器以获取其内容;但是,这总是伴随着清除前一个容器的保留。因此,总的来说,我们一次只有 3 个额外的容器,这将再次导致 O(1) 的空间复杂度。

    现在程序的整体空间复杂度为 O(n)+O(n+2)+O(1),最终解为 O(n)。

    希望对你有帮助!

    【讨论】:

    • 这很有帮助,谢谢。但是,我们也有 result 数组,我们可以在其中跟踪所有解决方案三元组。那是额外的空间。问题是:可能添加到result 数组的可能解决方案的上限/最大数量是多少?
    • 真诚的道歉,我错过了。我已经更正了我的答案。
    【解决方案3】:

    我同意您对时间复杂度的看法。您在循环中有一个循环,并且您总是将内部循环中的指针移动至少 1(leftright)。所以,就像你说的,O(n^2 / 2) 或 O(n^2)

    编辑:对于空间复杂性,我同意 Tassle 的回答

    【讨论】:

    • 啊,很好。时间复杂度如何?
    • 更新了我的答案。我完全同意你关于时间复杂度的想法,所以我没有在最初的回答中添加它。
    • 我不同意通常只有线性数量的三元组。可以有二次方(见我的回答)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 2012-04-09
    • 2020-05-03
    • 1970-01-01
    • 2023-03-11
    • 2020-11-15
    相关资源
    最近更新 更多