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

力扣题目链接 (opens new window)

# 题目描述

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

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

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

示例 1:

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

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
1
2

提示:

  • 2 <= nums.length <= 10^5
  • -30 <= nums[i] <= 30
  • 保证 数组 nums 之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内

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

# 思路

很多录友第一反应是,先求出所有元素的乘积total,然后answer[i] = total / nums[i]。

但题目明确说了不要使用除法。为什么呢?因为如果数组中有0,除法就会出问题。

那不用除法怎么做呢?

其实answer[i]就是nums[i]左边所有元素的乘积,再乘以nums[i]右边所有元素的乘积。

所以我们可以用两个数组,一个记录每个位置左边所有元素的乘积,一个记录右边所有元素的乘积。

定义:

  • prefix[i]:表示nums[0]到nums[i-1]的乘积(i左边所有元素的乘积)
  • suffix[i]:表示nums[i+1]到nums[n-1]的乘积(i右边所有元素的乘积)

那么 answer[i] = prefix[i] * suffix[i]。

举个例子,nums = [1,2,3,4]:

index:     0  1  2  3
nums:      1  2  3  4
prefix:    1  1  2  6   (prefix[0]=1, prefix[1]=1, prefix[2]=1*2=2, prefix[3]=1*2*3=6)
suffix:   24 12  4  1   (suffix[3]=1, suffix[2]=4, suffix[1]=3*4=12, suffix[0]=2*3*4=24)
answer:   24 12  8  6   (answer[i] = prefix[i] * suffix[i])
1
2
3
4
5

这样就能O(n)时间内解决。

进阶要求是能不能O(1)空间呢?

可以!我们不需要prefix和suffix两个数组,直接用answer数组先存prefix,然后从右往左遍历时,用一个变量suffixProduct累积右边的乘积,边遍历边更新answer。

# 模拟过程

以下展示 nums = [1,2,3,4] 的计算过程:

第1步:构建前缀积

前缀积:answer[i] = nums[0] × nums[1] × ... × nums[i-1]

第2步:累乘后缀积

从右往左遍历,用 suffixProduct 累积右边的乘积,同时更新 answer。最终得到结果 [24, 12, 8, 6]。

# 解题代码

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> answer(n, 1);
        
        // answer[i] 先存储 i 左边所有元素的乘积
        for (int i = 1; i < n; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }
        
        // 从右往左遍历,suffixProduct 累积右边的乘积
        int suffixProduct = 1;
        for (int i = n - 1; i >= 0; i--) {
            answer[i] *= suffixProduct;  // answer[i] = 左边乘积 * 右边乘积
            suffixProduct *= nums[i];    // 更新右边乘积
        }
        
        return answer;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 复杂度分析

  • 时间复杂度:O(n),两次遍历
  • 空间复杂度:O(1),不计算答案数组

# 其他语言

# Python3

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        answer = [1] * n
        
        # answer[i] 先存储 i 左边所有元素的乘积
        for i in range(1, n):
            answer[i] = answer[i - 1] * nums[i - 1]
        
        # 从右往左遍历,suffix_product 累积右边的乘积
        suffix_product = 1
        for i in range(n - 1, -1, -1):
            answer[i] *= suffix_product  # answer[i] = 左边乘积 * 右边乘积
            suffix_product *= nums[i]    # 更新右边乘积
        
        return answer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Java

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] answer = new int[n];
        
        // answer[i] 先存储 i 左边所有元素的乘积
        answer[0] = 1;
        for (int i = 1; i < n; i++) {
            answer[i] = answer[i - 1] * nums[i - 1];
        }
        
        // 从右往左遍历,suffixProduct 累积右边的乘积
        int suffixProduct = 1;
        for (int i = n - 1; i >= 0; i--) {
            answer[i] *= suffixProduct;  // answer[i] = 左边乘积 * 右边乘积
            suffixProduct *= nums[i];    // 更新右边乘积
        }
        
        return answer;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Go

func productExceptSelf(nums []int) []int {
    n := len(nums)
    answer := make([]int, n)
    
    // answer[i] 先存储 i 左边所有元素的乘积
    answer[0] = 1
    for i := 1; i < n; i++ {
        answer[i] = answer[i-1] * nums[i-1]
    }
    
    // 从右往左遍历,suffixProduct 累积右边的乘积
    suffixProduct := 1
    for i := n - 1; i >= 0; i-- {
        answer[i] *= suffixProduct  // answer[i] = 左边乘积 * 右边乘积
        suffixProduct *= nums[i]    // 更新右边乘积
    }
    
    return answer
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# JavaScript

var productExceptSelf = function(nums) {
    const n = nums.length;
    const answer = new Array(n).fill(1);
    
    // answer[i] 先存储 i 左边所有元素的乘积
    for (let i = 1; i < n; i++) {
        answer[i] = answer[i - 1] * nums[i - 1];
    }
    
    // 从右往左遍历,suffixProduct 累积右边的乘积
    let suffixProduct = 1;
    for (let i = n - 1; i >= 0; i--) {
        answer[i] *= suffixProduct;  // answer[i] = 左边乘积 * 右边乘积
        suffixProduct *= nums[i];    // 更新右边乘积
    }
    
    return answer;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 与代码随想录联系

这道题用到了前缀和(前缀积)的思想。

我在代码随想录数组章节里,讲解过前缀和的应用,可以看一下代码随想录数组章节。

本题的巧妙之处在于,用一个数组同时存储前缀积和累乘后缀积,实现了O(1)空间复杂度。这种技巧在很多数组问题中都有应用。

上次更新:: 10/25/2026, 10:24:40 PM

评论

验证登录状态...