238. 除自身以外数组的乘积

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

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

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

这个问题要求不使用除法,并且要在 O(n) 时间内完成。我们可以通过 前缀积后缀积 的方法来解决。


思路分析

对于每个位置 i,我们想要的结果是:

answer[i] = (nums[0] * nums[1] * ... * nums[i-1]) * (nums[i+1] * ... * nums[n-1])
           = 前缀乘积 * 后缀乘积

方法

  1. 先从左到右计算每个位置左侧所有元素的乘积(前缀积)
  2. 再从右到左计算每个位置右侧所有元素的乘积(后缀积),同时与左侧乘积相乘得到最终结果

为了 O(1) 额外空间(除了输出数组),我们可以:

  • 先用输出数组存储前缀积
  • 再用一个变量从右到左累乘后缀积,同时更新输出数组

算法步骤

  1. 初始化 answer[0] = 1
  2. 从左到右遍历:answer[i] = answer[i-1] * nums[i-1](计算前缀积)
  3. 初始化 suffix = 1
  4. 从右到左遍历:answer[i] *= suffixsuffix *= nums[i](乘以后缀积)

代码实现

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    const n = nums.length;
    const answer = new Array(n);
    
    // 第一步:计算每个位置左侧所有元素的乘积
    answer[0] = 1;
    for (let i = 1; i < n; i++) {
        answer[i] = answer[i - 1] * nums[i - 1];
    }
    
    // 第二步:乘上右侧所有元素的乘积
    let suffix = 1;
    for (let i = n - 1; i >= 0; i--) {
        answer[i] *= suffix;
        suffix *= nums[i];
    }
    
    return answer;
};

示例演示

例子nums = [1,2,3,4]

第一步(前缀积)

answer[0] = 1
answer[1] = answer[0] * nums[0] = 1 * 1 = 1
answer[2] = answer[1] * nums[1] = 1 * 2 = 2  
answer[3] = answer[2] * nums[2] = 2 * 3 = 6
此时 answer = [1, 1, 2, 6]

第二步(乘后缀积)

i=3: answer[3] *= 1 = 6*1=6, suffix=1*4=4
i=2: answer[2] *= 4 = 2*4=8, suffix=4*3=12
i=1: answer[1] *= 12 = 1*12=12, suffix=12*2=24  
i=0: answer[0] *= 24 = 1*24=24, suffix=24*1=24
最终 answer = [24, 12, 8, 6]

验证:

  • 24 = 234
  • 12 = 134
  • 8 = 124
  • 6 = 123

复杂度分析

  • 时间复杂度:O(n) - 两次遍历
  • 空间复杂度:O(1) - 除了输出数组外只用了常数空间

这种方法高效且满足题目要求,是最优解法。

posted @ 2025-11-02 12:29  阿木隆1237  阅读(16)  评论(0)    收藏  举报