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

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]

提示:

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

📝 核心笔记:除自身以外数组的乘积 (左右乘积列表)

1. 核心思想 (一句话总结)

"左边乘积x 右边乘积"。

想知道除了自己以外所有人的乘积,等于 (自己左边所有人的乘积) x (自己右边所有人的乘积)。

💡 直观理解:

不要试图去"剔除"自己(不用除法)。

而是把自己当成分界线,分别计算左半边和右半边的累积结果,最后左右一拍即合。

2. 算法流程 (三步走)
  1. 算左侧 ( pre**):** 从左往右扫,pre[i]0i-1 的乘积。
  2. 算右侧 ( suf**):** 从右往左扫,suf[i]i+1n-1 的乘积。
  3. 合并 ( ans**):** ans[i] = pre[i] * suf[i]

🔍 代码回忆清单 (带注释版)

复制代码
// 题目:LC 238. 除自身以外数组的乘积
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        
        // 1. 计算左侧乘积 (前缀积)
        int[] pre = new int[n];
        pre[0] = 1; // 关键点:最左边元素的左侧没有数字,视为 1
        for (int i = 1; i < n; i++) {
            pre[i] = pre[i - 1] * nums[i - 1]; // 注意是乘 nums[i-1]
        }

        // 2. 计算右侧乘积 (后缀积)
        int[] suf = new int[n];
        suf[n - 1] = 1; // 关键点:最右边元素的右侧没有数字,视为 1
        for (int i = n - 2; i >= 0; i--) {
            suf[i] = suf[i + 1] * nums[i + 1]; // 注意是乘 nums[i+1]
        }

        // 3. 合并结果
        int[] ans = new int[n];
        for (int i = 0; i < n; i++) {
            ans[i] = pre[i] * suf[i]; // 左积 * 右积
        }
        return ans;
    }
}

⚡ 快速复习 CheckList (易错点)

  • \] **边界初始值?** `pre[0]` 和 `suf[n-1]` 必须初始化为 **1**(乘法的单位元)。

    • pre1 开始(因为 0 已知)。
    • sufn-2 开始(因为 n-1 已知)。
  • \] **索引错位?** 计算 `pre[i]` 时乘的是 `nums[i-1]`(它的前一个数);计算 `suf[i]` 时乘的是 `nums[i+1]`(它的后一个数)。

你当前的代码使用了 O(N) 的额外空间(pre 和 suf 数组)。

面试官通常会追问:如何用 O(1) 额外空间(输出数组不计入)?

优化思路:

  1. 先把 ans 数组当做 pre 数组用(先算左侧)。

  2. 不创建 suf 数组,而是用一个变量 R (right) 动态维护右侧乘积。

  3. 从右往左倒着扫,边算 R 边更新 ans

    // 空间优化逻辑概览
    ans[i] = 左侧积; // 先存好左边
    int R = 1;
    for (int i = n-1; i >= 0; i--) {
    ans[i] = ans[i] * R; // 左边 * 右边
    R *= nums[i]; // R 累乘当前值,为下一个做准备
    }

相关推荐
Tisfy4 分钟前
LeetCode 0865.具有所有最深节点的最小子树:深度优先搜索(一次DFS + Python5行)
算法·leetcode·深度优先·dfs·题解
王老师青少年编程6 分钟前
信奥赛C++提高组csp-s之二分图
数据结构·c++·二分图·csp·信奥赛·csp-s·提高组
Q741_1478 分钟前
C++ 队列 宽度优先搜索 BFS 力扣 429. N 叉树的层序遍历 C++ 每日一题
c++·算法·leetcode·bfs·宽度优先
Yzzz-F9 分钟前
P4145 上帝造题的七分钟 2 / 花神游历各国[线段树 区间开方(剪枝) + 区间求和]
算法·机器学习·剪枝
零度@10 分钟前
Java 消息中间件 - ActiveMQ 保姆级全解2026
java·activemq·java-activemq
Zzz不能停11 分钟前
堆排序算法及大小堆区别
数据结构·算法
weixin_3993806913 分钟前
TongWeb异常宕机问题分析
java·tomcat
小鸡脚来咯13 分钟前
设计模式面试介绍指南
java·开发语言·单例模式
怦怦蓝17 分钟前
详解 IntelliJ IDEA 中编写邮件发送功能(从环境搭建到实战落地)
java·spring boot·intellij-idea