LeetCode 题解:除自身以外数组的乘积

在刷 LeetCode 的过程中,"除自身以外数组的乘积" 是一道经典的数组类题目,这道题不仅考察对数组遍历的理解,还要求在时间和空间复杂度上做到最优。今天就来分享这道题的高效解法,核心思路是通过两次遍历、利用两个变量分别记录左右侧乘积,实现时间复杂度 O (n)、空间复杂度 O (1)的最优解。

题目描述

给你一个长度为 n 的整数数组 nums,返回一个数组 ans,其中 ans [i] 等于 nums 中除 nums [i] 之外其余所有元素的乘积。要求不能使用除法,且在 O (n) 时间复杂度内完成,额外空间复杂度尽可能优化(除了答案数组外,仅使用常数级空间)。

解题思路

核心思想是拆分乘积计算:将 "除当前元素外所有元素的乘积" 拆分为 "当前元素左侧所有元素的乘积 × 当前元素右侧所有元素的乘积",通过两次遍历分别计算左右侧乘积,最终合并结果。

具体步骤:

  1. 定义两个变量 leftright,分别表示当前元素左侧所有元素的乘积、右侧所有元素的乘积,初始值均为 1(因为单个元素的左侧 / 右侧无元素,乘积为 1)。
  2. 定义答案数组 ans,长度与输入数组 nums 一致。
  3. 左到右遍历 :遍历每个元素时,先将当前 left(即该元素左侧所有元素的乘积)存入 ans[i],再更新 leftleft × nums[i](为下一个元素的左侧乘积做准备)。
  4. 右到左遍历 :遍历每个元素时,将 ans[i](左侧乘积)乘以当前 right(右侧乘积),得到最终的 "除自身外所有元素的乘积",再更新 rightright × nums[i](为前一个元素的右侧乘积做准备)。
  5. 遍历完成后,ans 数组即为最终结果。

复杂度分析

  • 时间复杂度:O (n),仅需两次遍历数组,n 为数组长度。
  • 空间复杂度:O (1),除了存储结果的 ans 数组外,仅使用了 leftright 两个常数级变量(题目通常允许忽略答案数组的空间消耗)。

Java 代码

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        // 定义答案数组
        int[] ans = new int[n];
        
        // 第一步:左到右遍历,计算每个元素左侧所有元素的乘积
        int left = 1;
        for (int i = 0; i < n; i++) {
            // 先将左侧乘积存入ans[i]
            ans[i] = left;
            // 更新left,为下一个元素的左侧乘积做准备
            left *= nums[i];
        }
        
        // 第二步:右到左遍历,计算右侧乘积并与左侧乘积相乘
        int right = 1;
        for (int i = n - 1; i >= 0; i--) {
            // 左侧乘积 × 右侧乘积 = 最终结果
            ans[i] *= right;
            // 更新right,为前一个元素的右侧乘积做准备
            right *= nums[i];
        }
        
        return ans;
    }
}

代码解析

  1. 初始化阶段

    • int n = nums.length; 获取数组长度,用于定义答案数组和遍历边界。
    • int[] ans = new int[n]; 初始化答案数组,存储最终结果。
  2. 左到右遍历

    • left 初始为 1,因为第一个元素左侧无元素,乘积为 1。
    • 遍历到第 i 个元素时,先把 left(左侧乘积)赋值给 ans[i],再让 left 乘以当前元素 nums[i],这样遍历结束后,ans[i] 存储的是第 i 个元素左侧所有元素的乘积。
  3. 右到左遍历

    • right 初始为 1,因为最后一个元素右侧无元素,乘积为 1。
    • 遍历到第 i 个元素时,将 ans[i](左侧乘积)乘以 right(右侧乘积),得到该位置的最终结果;再让 right 乘以当前元素 nums[i],为前一个元素的右侧乘积做准备。

示例验证

以输入 nums = [1,2,3,4] 为例:

  • 左到右遍历后,ans = [1, 1, 2, 6](分别对应每个元素左侧乘积:1、1、1×2、1×2×3)。
  • 右到左遍历:
    • i=3:ans[3] = 6 × 1 = 6right = 1×4=4
    • i=2:ans[2] = 2 × 4 = 8right = 4×3=12
    • i=1:ans[1] = 1 × 12 = 12right = 12×2=24
    • i=0:ans[0] = 1 × 24 = 24right = 24×1=24
  • 最终 ans = [24,12,8,6],符合 "除自身外所有元素乘积" 的结果。

总结

这道题的核心是拆分乘积计算,通过两次遍历分别处理左右侧乘积,避免了使用除法和额外的数组存储左右乘积,是空间优化的经典思路。关键点总结:

  1. 利用两个变量 leftright 分别记录当前元素的左右侧乘积,替代额外数组,实现空间复杂度 O (1);
  2. 两次遍历的顺序(先左后右)保证了每个位置的乘积能被逐步计算;
  3. 遍历过程中 "先赋值后更新" 的逻辑,确保了当前元素不会被包含到自身的左右乘积中。

这种思路不仅适用于这道题,也可以迁移到其他需要 "左右侧遍历" 的数组类题目中,希望能帮助大家理解和掌握~

相关推荐
数研小生2 小时前
构建命令行单词记忆工具:JSON 词库与艾宾浩斯复习算法的完美结合
算法·json
Python 老手2 小时前
Python while 循环 极简核心讲解
java·python·算法
@Aurora.2 小时前
优选算法【专题九:哈希表】
算法·哈希算法·散列表
爱看科技3 小时前
微美全息(NASDAQ:WIMI)研究拜占庭容错联邦学习算法,数据安全与隐私保护的双重保障
算法
qq_417129253 小时前
C++中的桥接模式变体
开发语言·c++·算法
YuTaoShao3 小时前
【LeetCode 每日一题】3010. 将数组分成最小总代价的子数组 I——(解法二)排序
算法·leetcode·排序算法
吴维炜5 小时前
「Python算法」计费引擎系统SKILL.md
python·算法·agent·skill.md·vb coding
Σίσυφος19006 小时前
PCL Point-to-Point ICP详解
人工智能·算法
玄〤6 小时前
Java 大数据量输入输出优化方案详解:从 Scanner 到手写快读(含漫画解析)
java·开发语言·笔记·算法