分析
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法, 且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
核心思想:
题解
左右辅助数组
开两个数组 。
- :下标 i 左侧全部元素乘积,
- :下标 i 右侧全部元素乘积,
代码
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> L(n);
vector<int> R(n);
vector<int> ans(n);
L[0] = 1;
for(int i = 1; i < n; i++) L[i] = L[i - 1] * nums[i - 1];
R[n - 1] = 1;
for(int i = n - 2; i >= 0; i--) R[i] = R[i + 1] * nums[i + 1];
for(int i = 0; i < n; i++) ans[i] = L[i] * R[i];
return ans;
}
};
时间复杂度:,三次线性遍历 空间复杂度:, 两个辅助数组
两趟遍历空间优化版(面试标准最优解)
复用输出数组 ans 充当左乘积数组;右侧乘积只用一个变量滚动,取消 R 数组。
题目说明:输出返回数组不计入额外空间复杂度。
代码
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n);
ans[0] = 1;
for(int i = 1; i < n; ++i){
ans[i] = ans[i-1] * nums[i-1];
}
// right 滚动变量,记录当前位置右侧乘积,代替R数组
int right = 1;
for(int i = n - 1; i >= 0; --i){
ans[i] = ans[i] * right; // 左乘积 * 右乘积 = 结果
right = right * nums[i]; // 更新右侧累积乘积
}
return ans;
}
};
时间复杂度:,两轮遍历 空间复杂度:,仅常数局部变量
双指针单轮遍历
用 lp 左累积乘积、rp 右累积乘积,左右指针同时向中间收拢,一轮循环完成计算。
代码
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> ans(nums.size(), 1);
int left = 0, right = nums.size() - 1;
int lp = 1, rp = 1;
while(right >= 0 && left <= nums.size()) {
ans[right] *= rp;
ans[left] *= lp;
lp *= nums[left++];
rp *= nums[right--];
}
return ans;
}
};
时间复杂度:,单轮循环 空间复杂度:
三种方案对比表
| 方案 | 时间复杂度 | 额外空间 | 特点 | 面试推荐度 |
|---|---|---|---|---|
| 左右辅助数组 | O(n) | O(n) | 逻辑直观 | ⭐⭐ |
| 两趟遍历优化版 | O(n) | O(1) | 可读性好,标准最优 | ⭐⭐⭐⭐⭐ |
| 双指针单轮 | O(n) | O(1) | 代码简洁,边界易错 | ⭐⭐ |

