文章目录
-
- 题目描述
- 解法一:暴力枚举(不推荐)
- [解法二:前缀积 + 后缀积](#解法二:前缀积 + 后缀积)
- 解法三:空间优化版(最佳方案)
- 总结
题目描述
给你一个整数数组 nums,请返回一个数组 answer,其中 answer[i] 等于 nums 数组中除 nums[i] 以外的其余各个元素的乘积。
要求:
- 不能使用除法(即不能通过总乘积再除当前元素来得到结果)。
- 必须在
O(n)时间复杂度内完成。 - 空间复杂度要求为
O(1)(输出数组不计入额外空间)。
示例:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
解释:
answer[0] = 2×3×4 = 24
answer[1] = 1×3×4 = 12
answer[2] = 1×2×4 = 8
answer[3] = 1×2×3 = 6
解法一:暴力枚举(不推荐)
原理:
对数组的每个元素,遍历剩余元素相乘得到结果。
流程图:
是
否
开始
遍历每个元素 i
初始化乘积 = 1
遍历所有元素 j
j == i ?
跳过
乘积 *= nums[j]
继续
存入答案数组
结束
复杂度分析:
- 时间复杂度:O(n²)
- 空间复杂度:O(n)
此方法效率极低,不满足题目要求。
解法二:前缀积 + 后缀积
核心思想:
-
prefix[i]表示从左到右的乘积:nums[0] × nums[1] × ... × nums[i-1] -
suffix[i]表示从右到左的乘积:nums[i+1] × nums[i+2] × ... × nums[n-1] -
最终结果:
answer[i] = prefix[i] * suffix[i]
示意图:
后缀积
suffix[3]=1
suffix[2]=A4
suffix[1]=A4*A3
suffix[0]=A4A3A2
前缀积
prefix[0]=1
prefix[1]=A1
prefix[2]=A1*A2
prefix[3]=A1A2A3
nums[0]
nums[1]
nums[2]
nums[3]
实现步骤:
- 构建前缀积数组。
- 构建后缀积数组。
answer[i] = prefix[i] * suffix[i]
复杂度:
- 时间复杂度:O(n)
- 空间复杂度:O(n)
解法三:空间优化版(最佳方案)
在上一解法中,我们可发现前缀积与后缀积的计算是线性独立的。
我们可以复用 answer 数组:
- 第一次遍历只保存前缀积;
- 第二次从右向左遍历时用一个临时变量保存后缀积,并直接乘入
answer[i]。
算法流程图:
开始
初始化 answer 为数组
前向遍历:计算前缀积
answer[i] = 前面元素乘积
初始化 suffix = 1
反向遍历
answer[i] *= suffix
suffix *= nums[i]
继续
结束,返回答案
代码实现(Java):
java
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] answer = new int[n];
// 第一遍:前缀积
answer[0] = 1;
for (int i = 1; i < n; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 第二遍:后缀积(反向遍历)
int suffix = 1;
for (int i = n - 1; i >= 0; i--) {
answer[i] *= suffix;
suffix *= nums[i];
}
return answer;
}
}
复杂度分析:
- 时间复杂度:
O(n) - 空间复杂度:
O(1)(输出数组不算额外空间)
总结
| 解法 | 思路描述 | 时间复杂度 | 空间复杂度 | 是否满足要求 |
|---|---|---|---|---|
| 解法一 | 暴力枚举 | O(n²) | O(n) | ❌ |
| 解法二 | 前缀积 + 后缀积 | O(n) | O(n) | ✅ |
| 解法三 | 空间优化版(前缀后缀合并) | O(n) | O(1) | ✅ ✅ |
最终推荐方案:
使用「空间优化版前后缀乘积法」,时间复杂度 O(n),空间复杂度 O(1),符合题目约束。