LeetCode - 238.除自身以外数组的乘积

题面

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。

说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

样例

1
2
输入: [1,2,3,4]
输出: [24,12,8,6]

思路

  • 常规做法
    一看到的一下子想到的可能就是把所有数累乘起来,然后循环一边,循环到那个数就除掉哪个数。但是注意如果这个题是累加的话可以这样做,但是这个题是累乘,如果有一个位置是0,这时候就会出错。

    所以考虑另一种方法,将数组做一次前缀和后缀累乘,循环到某个数的时候将该数前缀乘以后缀即可

  • 进阶做法
    在进阶里面要求到常数空间复杂度,如果是按照常规做法,需要用到left和right两个数组分别存前缀和后缀,另外还得开一个答案数组去存储计算答案。这样空间复杂度明显是 $O(n)$ ,因为题目要求中讲到

    出于对空间复杂度分析的目的,输出数组不被视为额外空间。

    也就是常数空间复杂度的话就需要把答案数组拿进来重新利用,于是可以想到如下做法

    假设nums[] = {a, b, c, d}
    令ans[] = {1, a, ab, abc}
    然后倒着计算一边ans数组使得
    ans[] = {1 * bcd, a * cd, ab * d, abc}

    这不刚好就是答案吗。这个题的做法就出来了

代码

  • 常规做法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    vector<int> productExceptSelf(vector<int>& nums) {
    int n = nums.size();
    vector<int> left(n), right(n), ans(n);

    left[0] = nums[0]; right[n - 1] = nums[n - 1];
    for(int i = 1; i < n; i++) { left[i] = nums[i] * left[i - 1]; }
    for(int i = n - 2; i >= 0; i--) { right[i] = nums[i] * right[i + 1]; }

    ans[0] = right[1]; ans[n - 1] = left[n - 2];
    for(int i = 1; i < n - 1; i++) {
    ans[i] = left[i - 1] * right[i + 1];
    }
    return ans;
    }
  • 进阶做法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    vector<int> productExceptSelf(vector<int>& nums) {
    int n = nums.size();
    vector<int> ans(n, 1);
    for(int i = 1; i < n; i++) {
    ans[i] = ans[i - 1] * nums[i - 1];
    }
    int tmp = nums[n - 1];
    for(int i = n - 2; i >= 0; i--) {
    ans[i] *= tmp;
    tmp *= nums[i];
    }
    return ans;
    }