LeetCode 238 除自身以外数组的乘积|无除法O(n)时间+O(1)空间双解法

文章前言

在数组算法面试高频题库中,除自身以外数组的乘积是一道经典的空间优化思维题,核心考点为「前缀后缀乘积拆分思想」,硬性约束两个关键点:禁止使用除法、时间复杂度控制在O(n),同时题目给出进阶要求:实现O(1)额外空间复杂度(输出数组不计入额外空间)。

很多初学者第一反应会直接计算数组总乘积,再遍历数组用总乘积 / numsi得到答案,但数组中存在 0 元素时,除法会直接触发报错,同时违背题目硬性规定,因此前缀 + 后缀乘积是唯一合规的解题路线。

题目原题梳理

给你一个整数数组 nums,返回数组 answer,其中 answeri 等于 nums 中除了 numsi 之外其余所有元素的乘积。

约束条件

1.禁止使用除法运算;

2.算法时间复杂度必须为O(n);

3.进阶:实现O(1)额外空间复杂度;

4.数组长度范围:2≤nums.length≤105;

5.元素值域:−30≤numsi≤30,结果保证在 32 位整型范围。

示例 1

输入:nums = 1,2,3,4

输出:24,12,8,6

示例 2

输入:nums = -1,1,0,-3,3

输出:0,0,9,0,0

核心解题思想拆解

对于数组任意下标i,除自身以外所有元素乘积可以拆分为两部分:

answeri=下标i左侧全部元素乘积×下标i右侧全部元素乘积

•数组最左侧元素:左侧无元素,左侧乘积初始值为1(乘法单位元,乘 1 不改变乘积结果)

•数组最右侧元素:右侧无元素,右侧乘积初始值为1

依靠左右乘积相乘,天然避开除法运算,完美适配题目限制。

解法一:前缀数组 + 后缀数组(基础入门版,适合新手理解)

算法思路

1.开辟两个辅助数组left\[\]、right\[\]

○lefti:存储下标i左侧所有元素的累积乘积

○righti:存储下标i右侧所有元素的累积乘积

2.正向遍历数组填充left前缀数组;

3.反向遍历数组填充right后缀数组;

4.遍历数组,ansi = lefti * righti得到最终结果。

实例演算(nums = 1,2,3,4

1.前缀数组left构造

left0 = 1

left1 = left0 * nums0 = 1

left2 = left1 * nums1 = 2

left3 = left2 * nums2 = 6

最终left = 1, 1, 2, 6

2.后缀数组right构造

right3 = 1

right2 = right3 * nums3 = 4

right1 = right2 * nums2 = 12

right0 = right1 * nums1 = 24

最终right = 24, 12, 4, 1

3.结果合并

ans0=1×24=24,ans1=1×12=12,ans2=2×4=8,ans3=6×1=6

输出结果24,12,8,6,与样例完全匹配。

Java 完整实现代码

java 复制代码
public class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        int[] left = new int[len];
        int[] right = new int[len];
        int[] res = new int[len];

        // 正向遍历计算左侧前缀乘积
        left[0] = 1;
        for (int i = 1; i < len; i++) {
            left[i] = left[i - 1] * nums[i - 1];
        }

        // 反向遍历计算右侧后缀乘积
        right[len - 1] = 1;
        for (int i = len - 2; i >= 0; i--) {
            right[i] = right[i + 1] * nums[i + 1];
        }

        // 左右乘积合并得到最终答案
        for (int i = 0; i < len; i++) {
            res[i] = left[i] * right[i];
        }
        return res;
    }
}

复杂度分析

•时间复杂度:O(n),三次线性遍历数组;

•空间复杂度:O(n),额外开辟两个长度为 n 的辅助数组;

优缺点总结

✅ 优点:逻辑直观,边界条件清晰,新手极易上手调试,适合课堂学习、算法入门练习;

❌ 缺点:额外数组占用大量内存,数组量级达到105时内存开销明显,无法满足题目进阶空间优化要求。

解法二:空间优化版(O (1) 额外空间,面试最优解)

优化核心逻辑

解法一中的前缀数组可以直接复用结果输出数组存储,不再单独创建left辅助数组;

右侧后缀乘积放弃数组存储,使用单个整型变量滚动更新,从后往前遍历数组直接完成乘积合并。

题目规定:输出结果数组不计入额外空间开销,整体仅使用 1 个临时变量,实现O(1)额外空间复杂度。

常规写法:结果数组存前缀,变量维护后缀

java 复制代码
public class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        // 第一步:正向遍历,结果数组承载前缀乘积
        ans[0] = 1;
        for (int i = 1; i < n; i++) {
            ans[i] = ans[i - 1] * nums[i - 1];
        }
        // 第二步:单变量滚动记录后缀乘积,反向遍历合并结果
        int suffix = 1;
        for (int i = n - 1; i >= 0; i--) {
            ans[i] = ans[i] * suffix;
            suffix = suffix * nums[i];
        }
        return ans;
    }
}

对称拓展写法:结果数组存后缀,变量维护前缀(正向遍历合并)

和常规写法逻辑完全镜像,先反向遍历让结果数组存储后缀乘积,再用前缀变量正向遍历相乘,写法同样合法正确:

java 复制代码
public class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        // 反向初始化后缀乘积存入结果数组
        ans[n - 1] = 1;
        for (int i = n - 2; i >= 0; i--) {
            ans[i] = ans[i + 1] * nums[i + 1];
        }
        // 正向遍历,前缀滚动变量合并结果
        int prefix = 1;
        for (int i = 0; i < n; i++) {
            ans[i] = ans[i] * prefix;
            prefix = prefix * nums[i];
        }
        return ans;
    }
}

复杂度分析

•时间复杂度:O(n),两次线性遍历,效率优于基础三次数组遍历;

•空间复杂度:O(1),仅使用 1 个临时变量,满足题目进阶最优要求;

优缺点总结

✅ 优点:极致空间压缩,适配大数据量场景,是面试场景的标准答案;两种镜像写法可根据个人遍历习惯自由选择;

❌ 缺点:滚动变量逻辑需要一定理解成本,新手初次阅读需要结合演算梳理流程。

两种解法横向对比表

踩坑点总结

1.严禁使用除法:数组包含 0 元素时,总乘积除法会直接抛出算术异常,直接违反题目硬性要求;

2.边界初始化值为 1:数组首尾单侧无元素,空乘积必须赋值为乘法单位元1,赋值 0 会直接导致整体结果全部归零;

3.滚动变量更新顺序:空间优化写法中,必须先完成当前位置乘积计算,再更新滚动乘积变量,顺序颠倒会覆盖有效数据导致计算错误。

文末总结

本题的核心精髓是空间复用 + 前后缀乘积拆分,从基础双数组实现理解原理,再进阶到常数空间优化,是数组类空间优化题型的经典模板。后续遇到「不能使用四则运算、需要拆分前后区间做乘积 / 求和」的算法题,都可以复用这套前后缀拆分思想解题。

相关推荐
依然鸣1 小时前
PTA团体程序设计天梯赛L2真题讲解L2-045-048
数据结构·c++·经验分享·学习·算法·pat考试·pat
丢掉幻想准备斗争2 小时前
5.5树与二叉树的应用
算法
ZhouDevin2 小时前
算法论文/模型微调2——仅骨干1%~3% 参数达到与全量微调相当的性能
算法
小林敲代码77882 小时前
Spring Boot 3 升级踩坑:aj-captcha 行为验证码底图加载失效
java·spring boot·后端
Sinosecu-OCR2 小时前
文通OCR技术深度解析:自研算法如何搞定复杂场景识别?
算法·ocr·ocr识别系统
带刺的坐椅2 小时前
Solon I18n:三种解析器,零样板代码
java·国际化·i18n·solon·多语言
掉鱼的猫3 小时前
Solon I18n:三种解析器,零样板代码
java
CQU_JIAKE3 小时前
8.5【A】
数据结构·算法
ESBK20253 小时前
学术邀约|CMICA 2026 第二届计算方法、智能控制与航空航天国际会议
大数据·人工智能·算法·飞行器·智能控制·航空航天·计算