LeetCode 238. 除自身以外数组的乘积
一、题目详情
原题链接:238. 除自身以外数组的乘积
给你一个长度为 n
的整数数组 nums
,其中 n > 1
,返回输出数组 output
,其中 output[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积。
示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
**提示:**题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。
说明: 请**不要使用除法,**且在 O(n)
时间复杂度内完成此题。
进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
二、左右乘积列表法
这道题要求在 O(n)
时间复杂度内完成,且不能使用除法。
而output[i]
等于 nums
数组中除 nums[i]
之外其余各元素的乘积,这个乘积可以拆分为 nums[i]
的前缀乘积和后缀乘积。
因此创建前缀乘积数组 left[nums.length]
和后缀乘积数组 right[nums.length]
,对于给定索引i
,left[i]
表示 nums[i]
左侧所有数字的乘积,right[i]
表示 nums[i]
右侧所有数字的乘积。对于数组 left
,left[0]
应该是 1
,因为第一个元素的左边没有元素。对于其他元素:left[i] = left[i-1] * nums[i-1]
。同理,对于数组 right
,right[nums.length]
应为 1
。其他元素:right[i] = right[i+1] * nums[i+1]
。
当前缀乘积数组left[nums.length]
和后缀乘积数组 right[nums.length]
填充完成后,在新建的输出数组output[nums.length]
进行迭代,且索引i
处的值为output[i] =left[i] * right[i]
。
class Solution {
public int[] productExceptSelf(int[] nums) {
int[] output = new int[nums.length];
//定义前缀乘积数组left和后缀乘积数组right
int[] left = new int[nums.length];
left[0] = 1;
int[] right = new int[nums.length];
right[nums.length - 1] = 1;
//为left数组和right数组赋值
for(int i = 1;i < nums.length;i++){
left[i] = left[i - 1] * nums[i - 1];
}
for(int i = nums.length - 2;i > -1;i--){
right[i] = right[i + 1] * nums[i + 1];
}
//填充output数组
for(int i = 0;i < nums.length;i++){
output[i] = left[i] * right[i];
}
return output;
}
}
三、优化空间复杂度
因为输出数组不被视为额外空间,因此可以将输出数组output
先初始化为前缀乘积数组left
,output[i]
表示 nums[i]
左侧所有数字的乘积。再用一个遍历来跟踪右边元素的乘积。并更新数组 answer[i]=answer[i] * R
。然后 R
更新为 R=R * nums[i]
,其中变量 R
表示的就是索引右侧数字的乘积。
class Solution {
public int[] productExceptSelf(int[] nums) {
int[] output = new int[nums.length];
//将left数组和right数组简化为output数组
output[0] = 1;
//先将output数组初始化为left数组
for(int i = 1;i < nums.length;i++){
output[i] = output[i - 1] * nums[i - 1];
}
//创建R作为辅助变量,完成和right数组的乘积
int r = 1;
for(int i = nums.length - 1;i > -1;i--){
output[i] *= r;
r *= nums[i];
}
return output;
}
}