echo

任生命穿梭 时间的角落

0%

除自身以外数组的乘积

238. 除自身以外数组的乘积

给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

示例:

1
2
输入: [1,2,3,4]
输出: [24,12,8,6]

提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。

说明:不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

一看到这个题,我们立刻想到求出数组中所有元素的乘积然后除以对应位置的元素,得到除自身以外的数组的乘积,但是很遗憾,我们并不能使用除法。

看到题目的提示中前缀和后缀,我们想到除自身以外数组的乘积就是数组中一个元素的前缀积乘后缀积,示例中元素 1 的前缀元素为 1 ,后缀元素为 2,3,4,最后得到结果 1 * 24 = 24。元素 3 的前缀元素为 1, 2,后缀元素为 4,最后得到结果 2 * 4 = 8。

我们可以先生成前缀积数组和后缀积数组,求两数组中对应位置元素的积就得到最后的答案。

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
class Solution {
public int[] productExceptSelf(int[] nums) {
int len = nums.length;
int[] L = new int[len];
int[] R = new int[len];
int[] ans = new int[len];

// 生成前缀数组
L[0] = 1;
for(int i = 1; i < len; i++){
L[i] = L[i - 1] * nums[i - 1];
}

// 生成后缀数组
R[len - 1] = 1;
for(int i = len - 2; i >= 0; i--){
R[i] = R[i + 1] * nums[i + 1];
}

for(int i = 0 ; i < len; i++){
ans[i] = L[i] * R[i];
}
return ans;
}
}

时间复杂度 O(n),空间复杂度O(n)。

我们可以直接使用 ans 数组来代替前缀数组,最后直接使用 ans 数组乘后缀数组得到结果。

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
class Solution {
public int[] productExceptSelf(int[] nums) {
int len = nums.length;

int[] R = new int[len];
int[] ans = new int[len];

// 生成前缀数组
ans[0] = 1;
for(int i = 1; i < len; i++){
ans[i] = ans[i - 1] * nums[i - 1];
}

// 生成后缀数组
R[len - 1] = 1;
for(int i = len - 2; i >= 0; i--){
R[i] = R[i + 1] * nums[i + 1];
}

for(int i = 0 ; i < len; i++){
ans[i] *= R[i];
}
return ans;
}
}

时间和空间复杂度不变。

更进一步,我们可以使用一个元素 R 来代替 后缀数组,在每次遍历时更新 R 的值即可实现和后缀数组相同的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int[] productExceptSelf(int[] nums) {
int len = nums.length;
int[] ans = new int[len];
int R = 1;
// 生成前缀数组
ans[0] = 1;
for(int i = 1; i < len; i++){
ans[i] = ans[i - 1] * nums[i - 1];
}

for(int i = len - 1 ; i >= 0; i--){
ans[i] *= R;
R *= nums[i];
}
return ans;
}
}

时间复杂度 O(n),空间复杂度O(1)。