题目:
给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积
注意:不能用除法,其实,如果数组里有0的话,用除法也是极其麻烦的;
输入:[1,2,3,4]
输出:[24,12,8,6]
我刚开始想的是,每个数肯定是由左边和右边乘积得到的,那么只要计算得到左边的乘积和,和右边的乘积和,就一定能得到总乘积和;但是我并没有想到先从左边遍历一遍,计算每个数左边的乘积和,然后再从右边开始遍历一遍,计算右边的乘积和,然后每个数就是左边的乘积和和右边的乘积和乘起来;
我想到,很可惜,我没有想到,不知道为啥,于是我想,那能不能用分治,每一次都把每个数的左边乘积和返回回来,右边的乘积和也返回回来,然后分别计算每一个数和相对边的乘积和相乘得到的数,作为这个数的和;
于是我的时间复杂度就为 :因为每一次都要计算一遍 k个分组的每个数,因此就是 O(n) +O( n/2) +O(n/4) +.......+1,累计和为
,因此时间复杂度为O(n);
class Solution {
public:
int Fenzhi(vector<int> &nums,vector<int> ©,int lo,int hi)
{
if(lo==hi){
copy[lo]=1; //是计算这个数,则它本身为1
return nums[lo]; //返回给别的要计算的数
}
int mi=lo+(hi-lo)/2;
int value=1;
int left=Fenzhi(nums,copy,lo,mi); //左边的所有乘积和
int right=Fenzhi(nums,copy,mi+1,hi); //右边的所有乘积和
for(int i=lo;i<=mi;++i)
{
copy[i]*=right; //将左边每个数和右边所有的乘积和计算一遍
}
for(int i=mi+1;i<=hi;++i)
{
copy[i]*=left; //将右边每个数和左边所有的乘积和计算一遍
}
return left*right;
}
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> copy(nums.size(),1);
Fenzhi(nums,copy,0,nums.size()-1);
return copy;
}
};
下面这种解法就是我刚才说的左积和右积的code
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size();
vector<int> v1(n,1),v2(n,1);
for(int i=1;i<n;i++){//求左积
v1[i]=v1[i-1]*nums[i-1];
}
for(int i=n-2;i>=0;i--){//求右积
v2[i]=v2[i+1]*nums[i+1];
}
for(int i=0;i<n;i++){//左积*右积
v1[i]*=v2[i];
}
return v1;
}
};
下面这种算法将对上面的代码进行了优化,使得在一次遍历中分别计算左边的乘积和,和右边的乘积和,它分别用两个指针来记录左边之前的乘积和,和右边之前的乘积和;
这样时间复杂度从O(3n)减小到 O(n)
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n=nums.size();
int left=1,right=1; //left:从左边累乘,right:从右边累乘
vector<int> res(n,1);
for(int i=0;i<n;++i) //最终每个元素其左右乘积进行相乘得出结果
{
res[i]*=left; //乘以其左边的乘积
left*=nums[i];
res[n-1-i]*=right; //乘以其右边的乘积
right*=nums[n-1-i];
}
return res;
}
};