LeetCode 238中等前后缀乘积
除自身以外数组的乘积 图解题解
不能用除法,还要求每个位置的「其余乘积」?前后缀各扫一遍,O(1) 额外空间解决。
想象你站在一排数字中间要算「除我以外所有数之积」。第一遍从左往右走:把你左侧所有数的累积积记进结果数组;第二遍从右往左,用一个滚动变量 right 追踪右侧累积积,每到一个位置把 right 乘进结果数组再更新 right。两遍扫完,除法没用过,额外变量只用了一个 right,输出数组之外零开销。
这道题到底在问什么
不使用除法,返回每个位置除自己外其它数的乘积。
- nums
- [1,2,3,4]
- 输出
- [24,12,8,6]
最优解:一步一步想明白
- 3关键不是背模板,而是看懂为什么这题适合前后缀乘积。
- 4求每位「除自己外所有数的乘积」。不用除法 → 答案[i] = 左边所有数之积 × 右边所有数之积。ans 先全置 1。
- 5第一遍从左到右:每位先放左边所有数的乘积。left 滚动累乘:ans = [1, 1, 1×2=2, 1×2×3=6]。
- 6第二遍从右往左,用一个变量 right(初值 1)记右边乘积。ans[3] ×= right(1) → 6(最右没有右边)。
- 7关键帧:继续向左,每位再乘上右边所有数的积(right 也滚动累乘):ans[2]×4=8、ans[1]×12=12、ans[0]×24=24。每位 = 左积 × 右积。
- 8最终 [24,12,8,6]。两遍扫、ans 复用做后缀积 → O(1) 额外空间(不含输出),全程无除法、不怕数组里有 0。
- 11把这句话记住,下次遇到同类题,就能更快选出方向。
- 16把这题练到能复述后,再去同类题里迁移:先复用状态定义,再调整边界条件。
⚠️ 容易写错的地方
✗ 错:直接把总乘积除以 nums[i]
✓ 对:用左右乘积,完全不做除法
有 0 时除法会崩,而且题目禁止除法
✗ 错:再开一个后缀积数组
✓ 对:用一个滚动变量 right 即可
第二遍从右往左只需一个变量记右积,省掉 O(n) 辅助数组。
✗ 错:忘了 ans 要先初始化
✓ 对:ans 全置 1 再开始累乘
乘法的单位元是 1;初值 0 会让结果全 0。
完整代码(Python / C++ / Java)
Python
class Solution:
def productExceptSelf(self, nums):
ans = [1] * len(nums)
left = 1
for i, x in enumerate(nums):
ans[i] = left
left *= x
right = 1
for i in range(len(nums) - 1, -1, -1):
ans[i] *= right
right *= nums[i]
return ansC++
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n, 1);
int left = 1;
for (int i = 0; i < n; i++) { ans[i] = left; left *= nums[i]; }
int right = 1;
for (int i = n - 1; i >= 0; i--) { ans[i] *= right; right *= nums[i]; }
return ans;
}
};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] = left; left *= nums[i]; }
int right = 1;
for (int i = n - 1; i >= 0; i--) { ans[i] *= right; right *= nums[i]; }
return ans;
}
}套路模板 · 前后缀乘积
# 前后缀乘积 通用检查表
# 1. 定义状态/指针/容器
# 2. 每轮只做一个清晰动作
# 3. 更新答案并处理边界class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n, 1);
int left = 1;
for (int i = 0; i < n; i++) { ans[i] = left; left *= nums[i]; }
int right = 1;
for (int i = n - 1; i >= 0; i--) { ans[i] *= right; right *= nums[i]; }
return ans;
}
};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] = left; left *= nums[i]; }
int right = 1;
for (int i = n - 1; i >= 0; i--) { ans[i] *= right; right *= nums[i]; }
return ans;
}
}复杂度
时间复杂度
O(n)
每个核心状态按算法要求处理固定次数
空间复杂度
O(1)
只保存必要的辅助结构或递归栈
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 除自身以外数组的乘积 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么不能用除法?+
题目明确禁止;且含 0 时除法会除零出错。前后缀积天然避开。
怎么做到 O(1) 额外空间?+
结果数组先存前缀积,再用一个变量从右往左累乘后缀积写回,不开第二个数组。
有 0 怎么办?+
前后缀积自动处理:一个 0 时只有它那位非零,两个及以上 0 时全为 0。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。