除自身以外数组的乘积
Array Product Except Self
开始编码给一个整数数组 nums,长度 N(2 ≤ N ≤ 10⁴,每个 nums[i] ∈ [-100, 100])。返回一个等长数组 result,使得 result[i] 等于 nums 中**除 nums[i] 自身外所有元素的乘积。这就是经典的 LeetCode 238:禁止用除法,算法必须能正确处理含 0(包括多个 0)的情况,目标复杂度是时间 O(n)、额外空间 O(1)**(输出数组不计入空间预算)。
干净的做法是对 nums 做两次线性扫描。第一遍(从左到右)把 out[i] 填成「前缀积」nums[0] * nums[1] * ... * nums[i-1]——也就是位置 i 严格左边那部分的乘积。第二遍(从右到左)维护一个标量 right 从 1 开始,每一步先做 out[i] *= right 再 right *= nums[i],最终 out[i] = (前缀积) * (后缀积) = 除自己以外所有元素的乘积。不用除法,除输出外不开新数组,所有 0 分布走同一条代码路径。
Examples
solution([1, 2, 3, 4]) 返回 [24, 12, 8, 6]。左扫把前缀积 [1, 1, 2, 6] 写进 out。右扫倒着走:i=3:out[3] = 6 * 1 = 6,right = 4;i=2:out[2] = 2 * 4 = 8,right = 12;i=1:out[1] = 1 * 12 = 12,right = 24;i=0:out[0] = 1 * 24 = 24。最终答案 [24, 12, 8, 6]。
solution([1, 2, 0, 4]) 返回 [0, 0, 8, 0]。位置 2 的那个 0 让 i > 2 的前缀积都为 0、i < 2 的后缀积也都为 0,所以除位置 2 外每个位置都被乘进了一个 0。位置 2 自己排除掉了那个 0,剩下 1 * 2 * 4 = 8。这正是除法解法会崩的情形。
solution([0, 0, 3]) 返回 [0, 0, 0]。数组里有两个 0,无论在哪个位置算「除自己外的乘积」,剩下的元素里都至少还有一个 0,所以输出全是 0。两遍扫法不需要任何「数 0」的分支判断。
签名见 stubs/stub.py。
Practical context
对一条流计算「除掉某一项后的聚合量」是时间序列分析与在线统计里的常见形状。这道题的形态直接出现在累计事件似然比里:给定每期的似然比 L_t(在假设 A 下相对假设 B 的密度比,恒正),「留一」乘积 prod(L) / L_t 表示在剔除第 t 期观测后,其余样本给出的累计似然——它是 jackknife 重采样、HMM 发射概率的留一交叉验证、以及序贯概率比检验里影响函数诊断的核心量。一旦某个 L_t 恰好为 0(一次制度切换、一次止损出场、一次截尾观测),/L_t 就崩了;前缀/后缀分解 prefix_t * suffix_t 不受影响,依然能在 O(n) 内一次性给出每个位置的留一乘积。同样的套路可以推广到留一求和(前缀和 + 后缀和,线性时间)、留一最大/最小(前缀 max / 后缀 max)、以及数值线性代数里的留一矩阵乘积。把这道题的标量乘积版本的「前缀+后缀双扫」吃透,思路可以直接迁移到区间最值查询、不上线段树的区间聚合、以及协方差估计里「全对去对角」这种模式。
约束条件
- 2 ≤ N ≤ 10^4,N = len(nums)
- -100 ≤ nums[i] ≤ 100
- nums 任意子集的乘积都在 int64 范围内
- 禁止使用除法运算符
样例
Case 1 · typical: small ascending sequence
输入: [[1,2,3,4]]
期望: [24,12,8,6]
完整乘积是 24。每个位置的答案 = 24 / nums[i]:24/1=24, 24/2=12, 24/3=8, 24/4=6。但本题禁止用除法,所以要用前缀积乘后缀积:i=0 时左边空积 1 乘右边 2*3*4=24;i=1 时 1 * (3*4) = 12;以此类推。
Case 2 · with-zero: single zero element
输入: [[1,2,0,4]]
期望: [0,0,8,0]
数组里只有一个 0(位置 2)。除了位置 2 以外,其它位置的「除自己外的乘积」都包含这个 0,所以全是 0。位置 2 自己被排除,剩下 1*2*4 = 8。这正是为什么不能用除法——`total / nums[2]` 会除以 0。
Case 3 · multiple-zeros: two or more zeros
输入: [[0,0,3]]
期望: [0,0,0]
有两个 0(位置 0 和位置 1)。无论排除哪一个位置,剩下的乘积里至少还有一个 0,所以全部输出都是 0。前缀积/后缀积法天然处理这种情况。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · typical: small ascending sequence
输入: [[1,2,3,4]]
期望: [24,12,8,6]
完整乘积是 24。每个位置的答案 = 24 / nums[i]:24/1=24, 24/2=12, 24/3=8, 24/4=6。但本题禁止用除法,所以要用前缀积乘后缀积:i=0 时左边空积 1 乘右边 2*3*4=24;i=1 时 1 * (3*4) = 12;以此类推。
Case 2 · with-zero: single zero element
输入: [[1,2,0,4]]
期望: [0,0,8,0]
数组里只有一个 0(位置 2)。除了位置 2 以外,其它位置的「除自己外的乘积」都包含这个 0,所以全是 0。位置 2 自己被排除,剩下 1*2*4 = 8。这正是为什么不能用除法——`total / nums[2]` 会除以 0。
Case 3 · multiple-zeros: two or more zeros
输入: [[0,0,3]]
期望: [0,0,0]
有两个 0(位置 0 和位置 1)。无论排除哪一个位置,剩下的乘积里至少还有一个 0,所以全部输出都是 0。前缀积/后缀积法天然处理这种情况。