除自身以外数组的乘积
给你一个整数数组 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]
简单做法:双指针遍历
进阶做法:通过分别计算每个元素的前缀乘积和后缀乘积,然后将它们相乘得到结果。
方法思路
- 前缀乘积数组:创建一个数组
prefix,其中prefix[i]表示nums中索引i左侧所有元素的乘积(不包含nums[i])。 - 后缀乘积数组:创建一个数组
suffix,其中suffix[i]表示nums中索引i右侧所有元素的乘积(不包含nums[i])。 - 结果数组:遍历每个元素,将其前缀乘积和后缀乘积相乘,得到最终结果。
代码实现
function productExceptSelf(nums) {
const n = nums.length;
const answer = new Array(n).fill(1);
// 计算前缀乘积
let prefix = 1;
for (let i = 0; i < n; i++) {
answer[i] *= prefix;
prefix *= nums[i];
}
// 计算后缀乘积并与前缀乘积相乘
let suffix = 1;
for (let i = n - 1; i >= 0; i--) {
answer[i] *= suffix;
suffix *= nums[i];
}
return answer;
}
代码解释
- 初始化结果数组:
answer初始化为全 1,用于存储最终结果。 - 计算前缀乘积:
- 从左到右遍历
nums,用变量prefix记录当前元素左侧所有元素的乘积。 - 更新
answer[i]为prefix,然后将当前元素乘入prefix。
- 从左到右遍历
- 计算后缀乘积并合并结果:
- 从右到左遍历
nums,用变量suffix记录当前元素右侧所有元素的乘积。 - 将
answer[i]乘以suffix,然后将当前元素乘入suffix。
- 从右到左遍历
复杂度分析
- 时间复杂度:O(n),两次线性遍历。
- 空间复杂度:O(1)(除结果数组外,仅使用常数级额外空间)。
示例分析
输入 nums = [1, 2, 3, 4]:
- 计算前缀乘积:
prefix = 1,answer[0] = 1。prefix = 1*1 = 1,answer[1] = 1。prefix = 1*2 = 2,answer[2] = 2。prefix = 2*3 = 6,answer[3] = 6。- 此时
answer = [1, 1, 2, 6]。
- 计算后缀乘积并合并:
suffix = 1,answer[3] *= 1 → 6。suffix = 1*4 = 4,answer[2] *= 4 → 8。suffix = 4*3 = 12,answer[1] *= 12 → 12。suffix = 12*2 = 24,answer[0] *= 24 → 24。- 最终
answer = [24, 12, 8, 6]。
正确性验证
- 对于每个元素
nums[i],其结果为左侧元素乘积(前缀)与右侧元素乘积(后缀)的乘积,符合题目要求。 - 无需除法,且仅使用两次遍历,满足 O(n) 时间复杂度。
这种方法通过预处理前缀和后缀乘积,巧妙地避免了除法运算,高效解决了问题。