LeetCode热题100——238.除自身以外数组的乘积

40 阅读3分钟

题目描述

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

请 不要使用除法, 且在 O(n) 时间复杂度内完成此题。

示例:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

  提示:

  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30
  • 输入 保证 数组 answer[i] 在  32 位 整数范围内

进阶: 你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)

解法

前缀积和后缀积

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    const n = nums.length;
    const pre = Array(n);
    pre[0] = 1;
    for (let i = 1;i<n;++i){
        pre[i] = pre[i-1] * nums[i-1];//i左边的所有乘积
    }
    let suf = 1;
    for(let i = n-1;i>=0;--i){
        pre[i] *= suf;
        suf *= nums[i]; 
    }
    return pre;
};

题目求answer[i]为除i外数组其它所有元素的乘积,那么answer[i]可分解为:
answer[i] = pre[i] * suf[i]

  • pre[i]定义为i前面的所有元素的乘积

  • suf[i]定义为i后面的所有元素的乘积

即answer[i]等于i前面的所有元素的乘积乘以i后面所有元素的乘积

初始化pre[0]=suf[n-1]=0

因为nums[0]前面没有任何元素,nums[n-1]后面没有任何元素,而answer[i] = pre[i] * suf[i].当i=0时,很明显,answer[0] = suf[0],那么pre[0]应该初始化为1,同理suf[n-1]=1

所以我们的算法实现可以分为三部分:

  • 求i的前缀积
  • 求i的后缀积
  • 求answer[i]

这三步每步的时间复杂度都为O(n),总体复杂度O(3n),即O(n)

我们可以进一步优化,将后两步放在一个循环中实现,即先求出前缀积,然后在求后缀积的循环过程中同时求结果

具体实现:
1. 第一次遍历:计算前缀积(左边积)

遍历数组,将每个 nums[i]\text{nums}[i] 左侧所有元素的乘积计算出来,并存储到输出数组 pre[i] 中。

  • 从左向右迭代。

    • pre[0]\text{pre}[0] 初始化为 11
    • 对于 i>0i > 0pre[i]\text{pre}[i] 等于前一个位置的前缀积 pre[i1]\text{pre}[i-1] 乘以 nums[i1]\text{nums}[i-1]
pre[0] = 1;
for (let i = 1; i < n; ++i) {
    pre[i] = pre[i - 1] * nums[i - 1];
}

2. 第二次遍历:计算后缀积并求最终结果

从右向左迭代,同时利用变量 suf 实时计算后缀积,并将其乘入已存储在前缀积。

  • 初始化变量 suf = 1(代表 nums[N1]\text{nums}[N-1] 右侧的乘积)。

  • 从右向左遍历:

    1. 求结果: 将当前 pre[i]\text{pre}[i](左边积)乘以当前的 suf(右边积),得到最终结果 answer[i]\text{answer}[i]
    2. 更新后缀积:suf 乘上当前元素 nums[i]\text{nums}[i],使其包含 nums[i]\text{nums}[i],为下一个元素 nums[i1]\text{nums}[i-1] 的右边积做准备。
let suf = 1;
for(let i = n - 1; i >= 0; --i) {
    pre[i] *= suf; // 乘以当前维护的后缀积
    suf *= nums[i]; // 更新后缀积,用于下一轮 i-1 的计算
}

时间,空间复杂度

时间复杂度: O(N)\mathbf{O(N)}

两个for循环,O(2N),总体复杂度为O(N)

空间复杂度: O(1)\mathbf{O(1)}

输出数组 不被视为 额外空间,仅使用了常数额外空间

参考题解 leetcode.cn/problems/pr…