文章前言
在数组算法面试高频题库中,除自身以外数组的乘积是一道经典的空间优化思维题,核心考点为「前缀后缀乘积拆分思想」,硬性约束两个关键点:禁止使用除法、时间复杂度控制在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.滚动变量更新顺序:空间优化写法中,必须先完成当前位置乘积计算,再更新滚动乘积变量,顺序颠倒会覆盖有效数据导致计算错误。
文末总结
本题的核心精髓是空间复用 + 前后缀乘积拆分,从基础双数组实现理解原理,再进阶到常数空间优化,是数组类空间优化题型的经典模板。后续遇到「不能使用四则运算、需要拆分前后区间做乘积 / 求和」的算法题,都可以复用这套前后缀拆分思想解题。