题目描述 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法, 且在 O(*n*) 时间复杂度内完成此题。
示例 1:
1 2 输入: nums = [1,2,3,4 ] 输出: [24,12,8,6 ]
示例 2:
提示:
2 <= nums.length <= 105-30 <= nums[i] <= 30保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内基本思路 刚开始我想得比较简单,认为只需要把所有元素的乘积求出,再在循环里一个个除就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <stdlib.h> int * productExceptSelf (int * nums, int numsSize, int * returnSize) { int * answer = (int *)malloc (numsSize * sizeof (int )); if (answer == NULL ) { *returnSize = 0 ; return NULL ; } int product = 1 ; for (int i = 0 ; i < numsSize; i++) { product *= nums[i]; } for (int i = 0 ; i < numsSize; i++) { answer[i] = product / nums[i]; } *returnSize = numsSize; return answer; }
Line 16: Char 29: runtime error: division by zero [solution.c]把我拉回了现实:只要有个0,这段代码全线崩盘,而且根本没有修改的余地,故舍弃此方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int * productExceptSelf (int * nums, int numsSize, int * returnSize) { int leftPro[numsSize]; int rightPro[numsSize]; leftPro[0 ] = 1 ; for (int i = 1 ; i < numsSize; i++) { leftPro[i] = leftPro[i-1 ] * nums[i-1 ]; } rightPro[numsSize-1 ] = 1 ; for (int i = numsSize - 1 - 1 ; i >= 0 ; i--) { rightPro[i] = rightPro[i+1 ] * nums[i+1 ]; } *returnSize = numsSize; int * returnNums = (int *)malloc (sizeof (int ) * numsSize); for (int i =0 ; i < numsSize; i++) { returnNums[i] = leftPro[i] * rightPro[i]; } return returnNums; }
正确的思路应该像是这样,分别求出左边和右边序列的乘积加以乘法。
后来我在评论区看到这样一种思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int ra[100000 ];int * productExceptSelf (int * nums, int numsSize, int * returnSize) { for (int i = 0 ; i < numsSize; i++){ ra[i] = 1 ; } int pre = 1 , suf = 1 ; for (int i = 1 ; i < numsSize; i++){ pre *= nums[i - 1 ]; suf *= nums[numsSize - i]; ra[i] *= pre; ra[numsSize - i - 1 ] *= suf; } *returnSize = numsSize; return ra; } 作者:随心 链接:https: 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这个实现非常紧凑,核心是“前缀乘积 + 后缀乘积”的双向累积。
这种算法的思路可以这样理解:
先把结果数组 ra 初始化为全 1。 从左到右累计前缀乘积,并更新当前位置结果。 同时从右到左累计后缀乘积,更新对称位置结果。 一次循环完成双向累积,因此时间复杂度为 $O(n)$。 最后设置 *returnSize 并返回结果数组。 这种写法的好处在于:
空间开销低:除结果数组外,只使用了常数级辅助变量。
计算效率高:通过双向累积避免除法,且只需线性扫描。
对于这类题,建议优先建立“位置贡献”视角: 当前位置答案 = 左侧所有数的乘积 × 右侧所有数的乘积。