当前位置: 首页 > news >正文

【LeetCode 热题 100】238. 除自身以外数组的乘积——(解法一)前缀积与后缀积

Problem: 238. 除自身以外数组的乘积
题目:给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
不要使用除法,且在 O(n) 时间复杂度内完成此题。

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(N)
    • 空间复杂度:O(N)

整体思路

这段代码旨在解决一个经典的数组问题:除自身以外数组的乘积 (Product of Array Except Self)。问题要求计算一个 ans 数组,其中 ans[i] 等于输入数组 nums 中除 nums[i] 之外所有元素的乘积。一个重要的约束是,不能使用除法运算。

该算法采用了一种非常巧妙的 前缀积与后缀积 相结合的策略。它将复杂的计算分解为两个更简单的、可预处理的步骤。

  1. 核心思想:分解问题

    • 对于任意索引 ians[i] 的值可以看作是两部分的乘积:
      • 前缀积 (Prefix Product)nums[i] 左侧所有元素的乘积,即 nums[0] * nums[1] * ... * nums[i-1]
      • 后缀积 (Suffix Product)nums[i] 右侧所有元素的乘积,即 nums[i+1] * nums[i+2] * ... * nums[n-1]
    • 将这两部分相乘,ans[i] = (前缀积) * (后缀积),就得到了除 nums[i] 以外所有元素的乘积。
  2. 预处理:计算前缀积和后缀积数组

    • 算法创建了两个辅助数组 preMultisufMulti 来存储所有可能的前缀积和后缀积。
    • 计算前缀积:通过一次从左到右的遍历,计算 preMulti 数组。preMulti[i] 被设计用来存储 nums[0...i-1] 的乘积。状态转移关系是 preMulti[i+1] = preMulti[i] * nums[i],其基础是 preMulti[0] = 1(空前缀的乘积为1)。
    • 计算后缀积:通过一次从右到左的遍历,计算 sufMulti 数组。sufMulti[i] 被设计用来存储 nums[i+1...n-1] 的乘积。状态转移关系是 sufMulti[j-1] = sufMulti[j] * nums[j],其基础是 sufMulti[n] = 1(空后缀的乘积为1)。
  3. 合成最终结果

    • 在两个辅助数组都填充好之后,最后通过一次遍历来构建 ans 数组。
    • 对于每个索引 i,直接从预处理好的数组中取出相应的前缀积和后缀积相乘,即 ans[i] = preMulti[i] * sufMulti[i]

通过这种“空间换时间”的策略,算法避免了在计算每个 ans[i] 时都进行重复的乘法运算,从而实现了线性时间的解决方案。

完整代码

class Solution {/*** 计算一个数组,其中 ans[i] 等于 nums 中除 nums[i] 之外所有元素的乘积。* @param nums 输入的整数数组* @return 结果数组*/public int[] productExceptSelf(int[] nums) {int n = nums.length;// ans: 最终的结果数组int[] ans = new int[n];// preMulti: 存储前缀积。preMulti[i] 表示 nums[0]...nums[i-1] 的乘积。int[] preMulti = new int[n + 1];// sufMulti: 存储后缀积。sufMulti[i] 表示 nums[i+1]...nums[n-1] 的乘积。int[] sufMulti = new int[n + 1];// 初始化两个辅助数组的所有元素为 1。// 1 是乘法单位元,作为计算的起点(空前缀/后缀的积为1)。Arrays.fill(preMulti, 1);Arrays.fill(sufMulti, 1);// 步骤 1: 从左到右计算前缀积for (int i = 0; i < n; i++) {// preMulti[i+1] 是 preMulti[i] (nums[0...i-1]的积) 乘以当前元素 nums[i]preMulti[i + 1] = preMulti[i] * nums[i];}// 步骤 2: 从右到左计算后缀积// 注意循环从 n-1 开始,并且更新的是 j-1 的位置for (int j = n - 1; j > 0; j--) {// sufMulti[j-1] 是 sufMulti[j] (nums[j+1...n-1]的积) 乘以当前元素 nums[j]sufMulti[j - 1] = sufMulti[j] * nums[j];}// 步骤 3: 合成最终结果// 对于每个索引 i,其结果是其左侧所有元素的乘积与右侧所有元素的乘积的组合for (int i = 0; i < n; i++) {// ans[i] = (nums[0...i-1]的积) * (nums[i+1...n-1]的积)// 这恰好等于 preMulti[i] * sufMulti[i]ans[i] = preMulti[i] * sufMulti[i];}return ans;}
}

时空复杂度

时间复杂度:O(N)

  1. 初始化Arrays.fill 对两个长度为 N+1 的数组进行填充,时间复杂度为 O(N) + O(N) = O(N)。
  2. 前缀积计算:第一个 for 循环遍历 nums 数组一次,执行 N 次操作。时间复杂度为 O(N)
  3. 后缀积计算:第二个 for 循环从 n-11,执行 N-1 次操作。时间复杂度为 O(N)
  4. 结果合成:第三个 for 循环遍历 n 次以填充 ans 数组。时间复杂度为 O(N)

综合分析
算法的总时间复杂度由几个独立的线性扫描组成:O(N) + O(N) + O(N) + O(N)。因此,最终的时间复杂度是 O(N)

空间复杂度:O(N)

  1. 主要存储开销:算法创建了三个与输入规模相关的数组:ans, preMulti, 和 sufMulti
    • int[] ans = new int[n]: 占用了 O(N) 的空间。在一些语境下,输出结果不计入额外空间,但这里我们分析总占用。
    • int[] preMulti = new int[n + 1]: 占用了 O(N) 的额外辅助空间。
    • int[] sufMulti = new int[n + 1]: 占用了 O(N) 的额外辅助空间。

综合分析
算法所需的额外辅助空间主要由 preMultisufMulti 两个数组决定。因此,其空间复杂度为 O(N) + O(N) = O(N)

【LeetCode 热题 100】238. 除自身以外数组的乘积——(解法二)前缀积与后缀积+优化空间复杂度

http://www.dtcms.com/a/266736.html

相关文章:

  • 算法学习笔记:7.Dijkstra 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • 物联网数据安全区块链服务
  • AI Agent意图识别
  • 二维码驱动的独立站视频集成方案
  • Mysql+neo4j创建节点和关系
  • Linux操作系统之文件(三):缓冲区
  • Kubernetes 服务发布基础学习
  • OpenSSL 内存泄漏修复全景:119 个历史 Commit 的类型分析与防御启示
  • 人工智能及其十大优势 Artificial Intelligence: With Its Advantages
  • PyCharm 安装使用教程
  • (LeetCode 每日一题) 3307. 找出第 K 个字符 II (位运算、数学)
  • 电力市场化改革中的智能决策革命:大模型与交易双驱动模式探析
  • 【微服务】记录-登录信息传递过程
  • 【AI大模型】深入理解 Transformer 架构:自然语言处理的革命引擎
  • LangChain 全面入门
  • Java 大视界 -- Java 大数据实战:智能安防入侵检测的特征工程与模型融合全解析
  • 文本分词 nltk
  • 【Unity笔记01】基于单例模式的简单UI框架
  • PowerPoint 转 PDF 转换器:Python GUI 工具的深度解析
  • python高级变量XI
  • vue-39(为复杂 Vue 组件编写单元测试)
  • 行阶梯形矩阵和行最简形矩阵的区别
  • HTTP 缓存
  • suricata新增协议处理流程
  • Windows系统x86机器安装麒麟ARM系统(自用记录)
  • 批量印刷拼版助手Quite Imposing Plus:Adobe 专业PDF拼版插件
  • 2025微信小程序wxapkg解包全攻略
  • ESP32S3将摄像头映射到LCD屏
  • Android开发前的准备工作
  • ContextIQ 上线:助力 Copilot 深度洞察你的工作场景