一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
一、题目描述
给你一个整数数组 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) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)
题目来源:力扣
二、思路分析
题意不难理解,我们直接以示例 1 开始讲解,输入 nums = [1, 2, 3, 4] ,输出 answer = [24, 12, 8, 6] ,这是因为:
- 24 =
1 *2 * 3 * 4 - 12 = 1
* 2* 3 * 4 - 8 = 1 * 2
* 3* 4 - 6 = 1 * 2 * 3
* 4
看到这个,我们可以得知,answer[i] 是当前元素之前的所有元素积 * 当前元素之后的所有元素积
,因此我们只需要得到每个元素的前缀积和后缀积,然后将这两者对应相乘起来即可。
要得到每个元素的前缀积和后缀积,我们可以用两个数组分别存储,即从第二个元素记录前面元素的乘积(第一个元素的前缀积为 1 ,直接存储即可),以及从倒数第二个元素记录后面元素的乘积(最后一个元素的后缀积为 1 ,也是直接存储即可)。
得到前缀积和后缀积两个数组后,每个元素一一对应相乘起来即为答案数组了。
但题意还有进阶要求,除了输出数组不被视为额外空间,不能开辟额外的空间辅助解题,这样一来就需要改进方法了。
我们可以将后缀积数组省去,改为整型变量,标识后缀积,在求出前缀积的前提下,倒序遍历 nums 数组的同时乘上每个元素的后缀积,一样可以达到目的。代码中有加上注释,相信不难理解。
三、AC 代码
/**
* @param {number[]} nums
* @return {number[]}
*/
var productExceptSelf = function(nums) {
// 不能原地修改 nums 数组,因为要分别计算前缀积和后缀积,需要遍历两次 nums
// 第一次遍历 nums 时先用来存放每个元素的前缀积,等第二次遍历 nums 时乘上后缀积,即为答案数组
let answer = [];
// 第一个元素没有前缀
answer[0] = 1;
// 从第二个元素开始算前缀积
for (let i = 1; i < nums.length; i++) {
answer[i] = answer[i - 1] * nums[i - 1];
}
// 最后一个元素没有后缀,设为 1
let suffix = 1; // 后缀积的标识
// 前缀积乘上后缀积即是答案数组 answer 了
for (let i = nums.length - 1; i >= 0; i--) {
answer[i] *= suffix;
suffix *= nums[i]; // 更新后缀积
}
return answer;
};