# 238. 除了自身以外数组的乘积
# 题目描述
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。
题目数据 保证 数组 nums 之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
2
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
2
提示:
2 <= nums.length <= 10^5-30 <= nums[i] <= 30- 保证 数组
nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 输出数组 不被视为 额外空间。)
# 思路
很多录友第一反应是,先求出所有元素的乘积total,然后answer[i] = total / nums[i]。
但题目明确说了不要使用除法。为什么呢?因为如果数组中有0,除法就会出问题。
那不用除法怎么做呢?
其实answer[i]就是nums[i]左边所有元素的乘积,再乘以nums[i]右边所有元素的乘积。
所以我们可以用两个数组,一个记录每个位置左边所有元素的乘积,一个记录右边所有元素的乘积。
定义:
prefix[i]:表示nums[0]到nums[i-1]的乘积(i左边所有元素的乘积)suffix[i]:表示nums[i+1]到nums[n-1]的乘积(i右边所有元素的乘积)
那么 answer[i] = prefix[i] * suffix[i]。
举个例子,nums = [1,2,3,4]:
index: 0 1 2 3
nums: 1 2 3 4
prefix: 1 1 2 6 (prefix[0]=1, prefix[1]=1, prefix[2]=1*2=2, prefix[3]=1*2*3=6)
suffix: 24 12 4 1 (suffix[3]=1, suffix[2]=4, suffix[1]=3*4=12, suffix[0]=2*3*4=24)
answer: 24 12 8 6 (answer[i] = prefix[i] * suffix[i])
2
3
4
5
这样就能O(n)时间内解决。
进阶要求是能不能O(1)空间呢?
可以!我们不需要prefix和suffix两个数组,直接用answer数组先存prefix,然后从右往左遍历时,用一个变量suffixProduct累积右边的乘积,边遍历边更新answer。
# 模拟过程
以下展示 nums = [1,2,3,4] 的计算过程:
第1步:构建前缀积
前缀积:answer[i] = nums[0] × nums[1] × ... × nums[i-1]
第2步:累乘后缀积
从右往左遍历,用 suffixProduct 累积右边的乘积,同时更新 answer。最终得到结果 [24, 12, 8, 6]。
# 解题代码
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> answer(n, 1);
// answer[i] 先存储 i 左边所有元素的乘积
for (int i = 1; i < n; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 从右往左遍历,suffixProduct 累积右边的乘积
int suffixProduct = 1;
for (int i = n - 1; i >= 0; i--) {
answer[i] *= suffixProduct; // answer[i] = 左边乘积 * 右边乘积
suffixProduct *= nums[i]; // 更新右边乘积
}
return answer;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 复杂度分析
- 时间复杂度:O(n),两次遍历
- 空间复杂度:O(1),不计算答案数组
# 其他语言
# Python3
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
n = len(nums)
answer = [1] * n
# answer[i] 先存储 i 左边所有元素的乘积
for i in range(1, n):
answer[i] = answer[i - 1] * nums[i - 1]
# 从右往左遍历,suffix_product 累积右边的乘积
suffix_product = 1
for i in range(n - 1, -1, -1):
answer[i] *= suffix_product # answer[i] = 左边乘积 * 右边乘积
suffix_product *= nums[i] # 更新右边乘积
return answer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Java
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] answer = new int[n];
// answer[i] 先存储 i 左边所有元素的乘积
answer[0] = 1;
for (int i = 1; i < n; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 从右往左遍历,suffixProduct 累积右边的乘积
int suffixProduct = 1;
for (int i = n - 1; i >= 0; i--) {
answer[i] *= suffixProduct; // answer[i] = 左边乘积 * 右边乘积
suffixProduct *= nums[i]; // 更新右边乘积
}
return answer;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Go
func productExceptSelf(nums []int) []int {
n := len(nums)
answer := make([]int, n)
// answer[i] 先存储 i 左边所有元素的乘积
answer[0] = 1
for i := 1; i < n; i++ {
answer[i] = answer[i-1] * nums[i-1]
}
// 从右往左遍历,suffixProduct 累积右边的乘积
suffixProduct := 1
for i := n - 1; i >= 0; i-- {
answer[i] *= suffixProduct // answer[i] = 左边乘积 * 右边乘积
suffixProduct *= nums[i] // 更新右边乘积
}
return answer
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# JavaScript
var productExceptSelf = function(nums) {
const n = nums.length;
const answer = new Array(n).fill(1);
// answer[i] 先存储 i 左边所有元素的乘积
for (let i = 1; i < n; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 从右往左遍历,suffixProduct 累积右边的乘积
let suffixProduct = 1;
for (let i = n - 1; i >= 0; i--) {
answer[i] *= suffixProduct; // answer[i] = 左边乘积 * 右边乘积
suffixProduct *= nums[i]; // 更新右边乘积
}
return answer;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 与代码随想录联系
这道题用到了前缀和(前缀积)的思想。
我在代码随想录数组章节里,讲解过前缀和的应用,可以看一下代码随想录数组章节。
本题的巧妙之处在于,用一个数组同时存储前缀积和累乘后缀积,实现了O(1)空间复杂度。这种技巧在很多数组问题中都有应用。
评论
验证登录状态...