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] 存 0 到 i-1 的乘积。
  2. 算右侧 ( suf**):** 从右往左扫,suf[i] 存 i+1 到 n-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(乘法的单位元)。
  • 循环范围?
    • pre 从 1 开始(因为 0 已知)。
    • suf 从 n-2 开始(因为 n-1 已知)。
  • 索引错位? 计算 pre[i] 时乘的是 nums[i-1](它的前一个数);计算 suf[i] 时乘的是 nums[i+1](它的后一个数)。

🚀 进阶优化 (面试常考:O(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 累乘当前值,为下一个做准备
    }

相关推荐
2601_97070383几秒前
工业除湿机租赁:把一份租金合同拆开看
大数据·数据结构·算法·数据库架构·推荐算法
心中有你0214几秒前
Java实现Dijkstra最短路径算法(邻接表 + 优先队列)
java·数据库·算法
步行cgn33 分钟前
Spring MVC + Thymeleaf 模板解析失败:从 500 错误到页面正常显示
java·spring·mvc
ZhengEnCi1 小时前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
lgcgkCQ1 小时前
【AI 转型实战 1】SpringAI 入门:搭建基础大模型对话能力
java·ai·agent·rag·spring ai·mcp·langchain4j
Wang's Blog1 小时前
Java 项目实战:交友项目 - 用户体系集成总结
java·开发语言·交友
学到老活到老1 小时前
第 24 集:配置文件写了 80,Spring Boot 为什么读到 90?
java·后端·spring
Flynn Liang1 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
liminla!2 小时前
SQL 从入门到进阶:语法速查与实战练习(完整优化版)
数据库·sql·算法
tongyiixiaohuang2 小时前
供应商主数据同步实战:从易快报到金蝶云星空的单一策略落地
java·网络·数据库