文章目录
- 🍷1. 题目
- 🍸2. 算法原理
- 🍥解法一:暴力求解
- 🍥解法二:前缀和(积)
- 🍹3. 代码实现
🍷1. 题目
题目链接:238. 除自身以外数组的乘积 - 力扣(LeetCode)
给你一个整数数组 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
- 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
进阶: 你可以在 O(1)
的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)
🍸2. 算法原理
本题的意思是,要求出除了当前下标位置,其他位置的乘积
🍥解法一:暴力求解
暴力求解就是遍历整个数组,然后再遍历一次除了当前位置的其他位置元素乘积,整个的时间复杂度为O(n2)
🍥解法二:前缀和(积)
这里求除了当前位置的整个数组的乘积,可以理解为求前面一部的前缀积+后面一部分的后缀积
预处理:
f
表示前缀积,f[i]
表示[0,i-1]
区间内所有元素的乘积f[i] = f[i-1] * nums[i-1]
g
表示后缀积,g[i]
表示[i+1,n-1]
区间内所有元素的乘积g[i] = g[i+1] * nums[i+1]
这里因为
f[i]
的区间是[0,i-1]
,所以这里的i
,实际上是i-1
使用预处理之后的数组:
有了预处理的数组,我们只需让f[i]*g[i]
即可求出当前位置的值
细节问题:
这里因为要求的是乘积,所以
f[0]
这里要提前处理一下,f[0] = 1
;同理g[n-1] = 1
。
f
是从前往后,g
是从后往前
这个时间复杂度为O(n)+O(n)+O(n),可理解为O(n)
这里进阶是空间复杂度为O(1)求解,采用的也是前缀积和后缀积,用
ret
先装前缀积,然后从后往前乘以后缀积。
我们前面采用2个数组装前缀积和后缀积,可以理解得更清晰一些。
🍹3. 代码实现
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
int n = nums.size();
vector<int> f(n) , g(n);
//预处理前缀积
f[0] = 1;
g[n-1] = 1;
for(int i=1;i<n;i++)
f[i] = f[i-1] * nums[i-1];
//预处理后缀积
for(int i=n-2;i>=0;i--)
g[i] = g[i+1] * nums[i+1];
vector<int> ret(n);
for(int i=0;i<n;i++)
ret[i]=f[i]*g[i];
return ret;
}
};
优化空间复杂度:
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
int n = nums.size();
vector<int> ret(n,1);
int left = 1;
for(int i=1;i<n;i++)
{
left *= nums[i-1];
ret[i] = left;
}
int right = 1;
for(int i=n-2;i>=0;i--)
{
right*=nums[i+1];
ret[i]*=right;
}
return ret;
}
};