目录

LeetCode热题-100238.-除自身以外数组的乘积

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


题目:

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

图文解析:

https://i-blog.csdnimg.cn/direct/fe61d80b177a45e3b5fe2b83dc00d6c7.png

核心思想:分治思想

将问题分解为两个部分:

  1. 左边所有元素的乘积(前缀积)
  2. 右边所有元素的乘积(后缀积)

最终结果 = 左边乘积 × 右边乘积

为什么这样设计?

  • 直接计算每个位置的乘积需要O(n²)时间
  • 使用前缀和后缀数组可以将时间复杂度降为O(n)
  • 避免了重复计算,提高了效率

📊 示例演示

假设 nums = [1, 2, 3, 4]

步骤1:计算前缀积数组 l

i=0: l[0] = 1
i=1: l[1] = l[0] × nums[0] = 1 × 1 = 1
i=2: l[2] = l[1] × nums[1] = 1 × 2 = 2  
i=3: l[3] = l[2] × nums[2] = 2 × 3 = 6
l = [1, 1, 2, 6]

步骤2:计算后缀积数组 r

i=3: r[3] = 1
i=2: r[2] = r[3] × nums[3] = 1 × 4 = 4
i=1: r[1] = r[2] × nums[2] = 4 × 3 = 12
i=0: r[0] = r[1] × nums[1] = 12 × 2 = 24
r = [24, 12, 4, 1]

步骤3:合并结果

ret[0] = l[0] × r[0] = 1 × 24 = 24
ret[1] = l[1] × r[1] = 1 × 12 = 12
ret[2] = l[2] × r[2] = 2 × 4 = 8
ret[3] = l[3] × r[3] = 6 × 1 = 6
ret = [24, 12, 8, 6]

验证:确实 1×2×3×4=24,去掉每个位置自身后得到正确结果。

代码实现:

class Solution {
    /**
     * 计算数组中每个元素除自身外其他元素的乘积
     * 时间复杂度:O(n),空间复杂度:O(n)(输出数组不计入空间复杂度时为O(1))
     * 
     * @param nums 输入数组
     * @return 结果数组,其中ret[i] = nums[0] × nums[1] × ... × nums[i-1] × nums[i+1] × ... × nums[n-1]
     */
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        
        // 前缀积数组:l[i] 表示nums[0]到nums[i-1]的乘积
        // l[0] = 1 (因为0前面没有元素)
        // l[1] = nums[0]
        // l[2] = nums[0] × nums[1]
        // ...
        // l[i] = nums[0] × nums[1] × ... × nums[i-1]
        int[] l = new int[n];
        
        // 后缀积数组:r[i] 表示nums[i+1]到nums[n-1]的乘积
        // r[n-1] = 1 (因为n-1后面没有元素)
        // r[n-2] = nums[n-1]
        // r[n-3] = nums[n-2] × nums[n-1]
        // ...
        // r[i] = nums[i+1] × nums[i+2] × ... × nums[n-1]
        int[] r = new int[n];
        
        // 初始化边界值
        l[0] = 1;          // 第一个元素的前缀积为1(空乘积)
        r[n - 1] = 1;      // 最后一个元素的后缀积为1(空乘积)
        
        // 计算前缀积:从左到右遍历
        // 对于每个位置i,l[i] = l[i-1] × nums[i-1]
        for (int i = 1; i < n; i++) {
            l[i] = l[i - 1] * nums[i - 1];
        }
        
        // 计算后缀积:从右到左遍历
        // 对于每个位置i,r[i] = r[i+1] × nums[i+1]
        for (int i = n - 2; i >= 0; i--) {
            r[i] = r[i + 1] * nums[i + 1];
        }
        
        // 创建结果数组
        int[] ret = new int[n];
        
        // 合并前缀积和后缀积得到最终结果
        // ret[i] = l[i] × r[i] = (前缀积) × (后缀积)
        // = (nums[0]×...×nums[i-1]) × (nums[i+1]×...×nums[n-1])
        for (int i = 0; i < n; i++) {
            ret[i] = l[i] * r[i];
        }
        
        return ret;
    }
}