238_除自身以外数组的乘积(算法思路, 优化和复杂度分析)

81 阅读2分钟

题目描述

给你一个整数数组 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]

思路

productExceptSelf 函数的目的是对于给定的数组 nums,返回一个新数组 ans,其中 ans[i] 等于 nums 中除了 nums[i] 以外的所有元素的乘积。

  1. 构建左乘积数组 (left):
    • 初始化 left[0] = 1,因为对于第一个元素,没有左边的元素。
    • 对于每个 i1nums.length - 1,计算 left[i] = left[i - 1] * nums[i - 1]
    • 这样,left[i] 存储了 nums 数组中第 i 个元素左边所有元素的乘积。
  2. 构建右乘积数组 (right):
    • 初始化 right[num.length - 1] = 1,因为对于最后一个元素,没有右边的元素。
    • 对于每个 inums.length - 2到 0,计算 right[i] = right[i + 1] * nums[i + 1]
    • 这样,right[i] 存储了 nums 数组中第 i 个元素右边所有元素的乘积。

优化

我们可以直接使用1个遍历来存储右边元素的乘积,并对应乘以left[i],可以减少一次对数组的遍历和right数组的空间开销

计算右乘积并同时构建结果数组 (ans):

  • 初始化一个临时变量 temp = 1,用于存储右边所有元素的乘积。
  • 从数组的末尾开始遍历,对于每个i从 nums.length - 1到0:
    • ans[i] = left[i] * temp,即当前位置的结果等于左乘积乘以右乘积。
    • 更新 temptemp * nums[i],以便在下一次迭代中使用

复杂度分析

  • 时间复杂度: O(N)
    • 遍历数组两次(一次构建左乘积,一次计算右乘积并构建结果数组),总时间复杂度为线性级别。
  • 空间复杂度: O(1)(不计输出数组)
    • 只使用了固定数量的额外变量(如 left 数组和 temp 变量)。如果计入输出数组 ans,则空间复杂度为 O(N)

code

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    let left = [1];
    for(let i = 1; i < nums.length; i++){
        left[i] = left[i - 1] * nums[i - 1];
    }
    let temp = 1, ans = [];
    for(let i = nums.length - 1; i >= 0; i--){
        ans[i] = temp * left[i];
        temp *= nums[i];
    }
    return ans;
};

// 示例使用
const nums = [1, 2, 3, 4];
console.log(productExceptSelf(nums)); // 输出: [24, 12, 8, 6]