LeetCode 剑指 Offer 66. 构建乘积数组
题目描述


题意分析
给定数组
a,构造同长度数组,使第i项等于输入中除下标i之外所有元素的乘积。排除的是当前位置,其他位置即使与它值相同,也仍要参与相乘。题目禁止使用除法,输入还可能有零或负数。需要直接组合左右两侧的乘积;空数组返回空数组,某一侧没有元素时,其空乘积按一处理。
解法:前后缀乘积
核心思路
[!blue]
对任意位置
i,除自身以外的元素恰好分成两部分:严格位于左侧的元素,以及严格位于右侧的元素。答案就是这两部分乘积的乘积,不需要先计算全数组乘积再相除。第一遍从左到右,把左侧乘积写入结果数组。最左位置没有左侧元素,所以
res[0] = 1;之后使用res[i] = res[i - 1] * a[i - 1],在前一位置的左侧乘积中加入刚跨过的元素,仍然不包含当前位置。第二遍从右到左,令变量
right保存当前位置严格右侧的乘积。最右位置右侧为空,初始为一。先执行res[i] *= right,把两侧信息合起来;再执行right *= a[i],让下一轮左移后能把刚经过的当前元素算进右侧。两遍都只读取输入值,结果数组同时承担左侧预处理与最终输出,因此除了输出之外只需要一个滚动乘积变量。整个过程没有除法,零会自然进入应该包含它的位置;某个位置自身为零时,它自己的答案仍只包含其他位置的值。
解题步骤
- 输入为空时返回空数组,否则创建同长度结果并令首项为一。
- 从左向右,用前一结果乘前一输入值,填入每个位置严格左侧的乘积。
- 初始化右侧乘积
right = 1,从数组末尾向前扫描。- 先把
right乘入当前结果,再将当前输入值乘入right,供下一轮使用。- 返回已经合并左右乘积的结果。
代码实现
class Solution {
// 不能使用除法,因此不能通过总乘积直接相除。
public int[] constructArr(int[] a) {
if (a.length == 0) {
return new int[0];
}
int n = a.length;
int[] res = new int[n];
res[0] = 1;
for (int i = 1; i < n; i++) {
// 结果先只保存当前下标严格左侧的乘积。
res[i] = res[i - 1] * a[i - 1];
}
int right = 1;
for (int i = n - 1; i >= 0; i--) {
// 先乘入严格右侧的积,再把当前元素纳入下一轮。
res[i] = res[i] * right;
right *= a[i];
}
return res;
}
}
func constructArr(a []int) []int {
// 不能使用除法,因此不能通过总乘积直接相除。
if len(a) == 0 {
return []int{}
}
n := len(a)
res := make([]int, n)
res[0] = 1
for i := 1; i < n; i++ {
// 结果先只保存当前下标严格左侧的乘积。
res[i] = res[i-1] * a[i-1]
}
right := 1
for i := n - 1; i >= 0; i-- {
// 先乘入严格右侧的积,再把当前元素纳入下一轮。
res[i] = res[i] * right
right *= a[i]
}
return res
}
复杂度分析
设数组长度为 $n$。
- 时间复杂度:$O(n)$,前后各扫描一次。
- 辅助空间复杂度:$O(1)$,除返回数组外只维护常数个变量;结果数组占 $O(n)$。
关键点总结
[!green]
- 每个答案拆成严格左侧乘积与严格右侧乘积,始终排除当前元素。
- 结果数组先存左侧乘积,右侧只需一个变量从右向左维护。
- 先更新答案、再扩展右侧范围,空乘积用一作为起点。
易错点总结
[!yellow]
- 前缀转移应乘
a[i - 1],若乘a[i]就提前包含了自身。- 第二遍若先更新
right再写当前答案,也会把自身乘进去。- 乘积起点必须为一,设为零会让后续结果全部变零。
- 不能用总乘积除以当前值,既违反题意,也无法直接处理输入为零的位置。
- 输入长度为一时,排除自身后没有其他元素,按空乘积定义得到一。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 724. 寻找数组的中心下标 | 简单 | 同样把当前位置两侧拆成前缀与后缀,本题乘积排除自身,原题比较左右和。 |
| 42. 接雨水 | 困难 | 同样结合左右预处理信息,本题两侧相乘,原题由左右最高边界共同限制水位。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!