题目: 除自身以外数组的乘积
描述:
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
显然如果可以使用除法,那么我们计算所有元素的乘积sum,那么answer[i] = sum/nums[i]。那么我们考虑暴力法,直接求除了这个元素外的所有元素乘积,那么对于求每一个answer[i],我们都需要遍历一遍数组nums,那么 遍历n次,因此我们需要的时间复杂度为o(n²),超过了题目要求的o(n),时间复杂度,所以我们现在需要考虑的是只需要遍历常量遍数组nums,就可以得出answer的算法。
方法一:
我们知道answer[i]表示除了nums[i]以外所有元素的乘积,那么我们如果把他们按照相对nums[i]的位置,我们可以分为左边的部分left,和右边的部分right,那么answer[i]就能表示nums[i]左边部分的乘积乘以右边部分的乘积。顺着这个思路我们定义两个大小为n的vector:left,right.那么就有left[i]表示nums[i]左边所有元素的乘积,其中left[0] = 1(左边没有元素),right[i]表示nums[i]右边所有元素的乘积,同样的right[n-1] = 1(右边没有元素)。而left[i] = left[i-1]*nums[i-1],i-1左边所有的元素乘以i-1就是i左边所有的元素,同理rigjt[i] = right[i+1]*nums[i+1].最后answer[i] = left[i]*right[i].
时间复杂度o(n)
空间复杂度o(n)
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> left(n);
vector<int> right(n);
left[0] = 1;
right[n-1] = 1;
for(int i=1;i<n;i++){//初始化left数组,从初值left[0]开始往后
left[i] = left[i-1]*nums[i-1];
}
for(int i=n-2;i>=0;i--){//初始化right,从 初值right[n-1]往前
right[i] = right[i+1]*nums[i+1];
}
for(int i=0;i<n;i++){//求answer数组,这里直接用nums数组
nums[i] = left[i]*right[i];
}
return nums;
}
方法二:假设我们answer数组不算做本算法的存储空间,我们方法一的空间复杂度为o(n),我们需要不定义left数组和right数组,使得我们空间复杂度变为o(1),所以此方法是对方法一的改进,我们把answer作为我们的left数组,定义一个变量right,动态的更新right,使得其表示为右侧所有元素的乘积。
时间复杂度o(n)
空间复杂度o(1)
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> answer(n);//answer作为left数组
answer[0] = 1;//第一个元素左边没有元素,即为1
for(int i=1;i<n;i++){//初始化answer数组,即初始化left
answer[i] = answer[i-1]*nums[i-1];
}
int right = 1;
for(int i = n-1;i>=0;i--){
answer[i] = answer[i]*right;//左边元素乘积*右边元素乘积
right = right*nums[i];//动态更新right表示为右侧所有元素的乘积
}
return answer;
}