除自身以外数组的乘积算法详解
今天的题目是力扣面试经典150题中的数组的中等难度题:除自身以外数组的乘积。
题目链接:https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-interview-150
问题描述
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
-
示例 1:
-
输入:
nums = [1,2,3,4] -
输出:
[24,12,8,6] -
解释: 对于每个位置 i,output[i] 是 nums 中除 nums[i] 以外所有元素的乘积。
-
-
示例 2:
-
输入:
nums = [-1,1,0,-3,3] -
输出:
[0,0,9,0,0] -
解释: 对于每个位置 i,output[i] 是 nums 中除 nums[i] 以外所有元素的乘积。
-
题目分析
先看题目要求,给定数组,输出一个数组,并且这个数组的每个元素的值,是给定数组除当前位置外所有元素的乘积。
我们再看示例,也很明显:
- 24 = 2x3x4
- 12 = 1x3x4
- 8 = 1x2x4
- 6 = 1x2x3
- 0 = 1x0x-3x3
- 0 = -1x0x-1x3
- 9 = -1x1x-3x3
- 0 = -1x1x0x3
- 0 = -1x1x0x-3
那么,对题目要求我们已经明确了,再看题目其他信息:请不要使用除法,且在 O(n) 时间复杂度内完成此题。
正常情况下,这种求除自身元素外乘积的题目,直接所有元素相乘再除自身就能得到答案(求和,差,商类似),但是题目要求我们不能用除法,所以这个解法直接排除。
在 O(n) 时间复杂度内完成此题,这句话就明确了,我们解答题目,最多只能一层for循环,这样才能保证时间复杂度在O(n),但是题目没有要求空间复杂度,所以我们考虑用多个数组来实现解题。
解题思路
题目要求新数组元素的值是除自身外的所有元素的乘积,是不是可以理解为 元素左边的所有元素的乘积与元素右边所有元素的乘积再相乘呢?另外,由于首个位置的元素和最后位置的元素缺失一边的元素乘积,为了不影响结果,我们可以设定缺失的那边值为1。
由此,我们针对示例的分析可以修改如下:
- 24 = 1(假定的左边乘积) x (2x3x4)(右边的乘积)
- 12 = 1(左边的乘积)x(3x4)(右边的乘积)
- 8 = (1x2)(左边的乘积)x4(右边的乘积)
- 6 = (1x2x3)(左边的乘积)x1(假定的右边乘积)
另外在分析的时候,我们已经考虑了用多个数组实现解题,因为多个数组分多次循环,不改变时间复杂度为O(n),只是影响空间复杂度。
所以我们可以直接设定两个数组,一个数组计算元素左边的乘积,一个元素计算右边的乘积,最后相乘,即可得到答案。
当然,实际计算的时候,我们在计算第二个数组的乘积的时候,将第一个数组的乘积相乘即可。
实际算法代码
以下是使用上述思路的 Java 实现:
public class ProductExceptSelf {
public static void main(String[] args) {
ProductExceptSelf solution = new ProductExceptSelf();
int[] nums1 = {1, 2, 3, 4};
int[] result1 = solution.productExceptSelf(nums1);
System.out.println(Arrays.toString(result1)); // 输出: [24, 12, 8, 6]
int[] nums2 = {-1, 1, 0, -3, 3};
int[] result2 = solution.productExceptSelf(nums2);
System.out.println(Arrays.toString(result2)); // 输出: [0, 0, 9, 0, 0]
}
public int[] productExceptSelf(int[] nums) {
int length = nums.length;
int[] answer = new int[length];
// 从左向右计算左侧所有元素的乘积
answer[0] = 1;
for (int i = 1; i < length; i++) {
answer[i] = nums[i - 1] * answer[i - 1];
}
// 从右向左计算右侧所有元素的乘积
int R = 1;
for (int i = length - 1; i >= 0; i--) {
answer[i] = answer[i] * R;
R *= nums[i];
}
return answer;
}
}
结果
运行代码,结果符合:
提交到力扣,也通过测试:
总结
通过两个数组的方式,我们分别对左右相乘,再将最终结果相乘,成功得到答案,类似的和,差,商的题目一样的解法。做这些题目,一定要归纳总结出一些自己的东西,而不是盲目的解题,看解析。
加油!!!