题面
给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
进阶:你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
样例
1 | 输入: [1,2,3,4] |
思路
常规做法
一看到的一下子想到的可能就是把所有数累乘起来,然后循环一边,循环到那个数就除掉哪个数。但是注意如果这个题是累加的话可以这样做,但是这个题是累乘,如果有一个位置是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
14vector<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
13vector<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;
}