238. 除自身以外数组的乘积
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
提示:
2 <= nums.length <= 105
-30 <= nums[i] <= 30
- 输入 保证 数组
answer[i]
在 32 位 整数范围内
思路分析
如果用除法的话,可以先算所有数的乘积,然后每个位置除以自己。但题目中不允许使用除法,我想到的另一个方法是前缀和后缀的乘积。比如,对于每个元素 i 来说,左边所有元素的乘积乘上右边所有元素的乘积,就是结果。那这样,先从左到右计算每个元素的左边乘积,存到一个数组里,然后从右到左计算右边乘积,再乘到对应的左边乘积上,得到最终结果。
具体步骤
- 初始化一个answer数组,长度和nums一样。
- 计算左边的乘积,从左到右遍历。首先answer[0] = 1,然后后面的每个元素 i ,answer[i] = answer[i-1] * nums[i-1]。这样answer数组此时保存的是每个元素的左边乘积。
- 然后计算右边的乘积,用一个变量rightProduct来保存右边的累积,初始化为1。
- 从右往左遍历,每次将answer[i]乘以rightProduct,然后更新rightProduct *= nums[i]。这样,在遍历过程中answer[i] = left[i] * rightProduct。其中rightProduct是右边所有元素的乘积。
这样的话,整个过程是两次遍历。
首先初始化answer数组。第一个循环,从左到右填充左边乘积。然后第二个循环,从右到左,用rightProduct变量来乘。具体步骤:
初始化answer数组,长度为nums.length。answer[0] = 1。然后对于i从1到nums.length-1,answer[i] = answer[i-1] * nums[i-1]。
然后初始化rightProduct为1。然后从i=nums.length-1到0,循环。每次将answer[i]乘以rightProduct,然后rightProduct *= nums[i]。
程序代码
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] answer = new int[n];
answer[0] = 1;
// 计算每个元素的左边乘积
for(int i = 1; i < n; i++){
answer[i] = answer[i - 1] * nums[i - 1];
}
// 计算右边乘积并乘以左边乘积
int rightProduct = 1;
for(int i = n - 1; i >= 0; i--){
answer[i] *= rightProduct;
rightProduct *= nums[i];
}
return answer;
}
}
-
步骤分解:
-
前缀乘积计算:从左到右遍历数组,
answer[i]
存储nums[i]
左边所有元素的乘积。 -
后缀乘积整合:从右到左遍历数组,使用变量
rightProduct
动态维护当前元素右边的乘积,并直接将其乘到answer[i]
上。
-
-
复杂度:两次遍历,时间复杂度O(n);结果数组外额外空间O(1),满足题目要求。