一、题目描述
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums =[1,2,3,4]
输出:[24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
提示:
2 <= nums.length <= 10^5
-30 <= nums[i] <= 30
- 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
二、解题思路
- 创建一个结果数组
answer
,长度与输入数组nums
相同。 - 初始化两个变量
left
和right
,分别表示当前元素左侧所有元素的乘积和右侧所有元素的乘积。初始时,left
为1,right
为数组最后一个元素的值。 - 遍历数组
nums
,对于每个元素nums[i]
,计算answer[i] = left * right
。 - 在遍历过程中,更新
left
和right
的值。对于left
,每遍历一个元素,将left
乘以当前元素;对于right
,每遍历一个元素,将right
乘以倒数第i+1
个元素。 - 返回结果数组
answer
。
三、具体代码
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] answer = new int[n];
// 初始化left和right
int left = 1, right = 1;
// 计算每个元素的左侧乘积
for (int i = 0; i < n; i++) {
answer[i] = left;
left *= nums[i];
}
// 计算每个元素的右侧乘积,并与左侧乘积相乘
for (int i = n - 1; i >= 0; i--) {
answer[i] *= right;
right *= nums[i];
}
return answer;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 第一个
for
循环遍历了数组nums
一次,其时间复杂度为O(n),其中n是数组nums
的长度。 - 第二个
for
循环同样遍历了数组nums
一次,其时间复杂度也是O(n)。
由于这两个循环是顺序执行的,因此总的时间复杂度是两个循环时间复杂度的和,即O(n) + O(n) = O(n)。
2. 空间复杂度
- 输出数组
answer
占用了O(n)的空间,因为它需要存储与输入数组nums
相同长度的结果。 - 除了输出数组之外,代码中使用的额外空间只有几个变量(
n
,left
,right
),这些变量占用常数级别的空间,即O(1)。
因此,总的空间复杂度是输出数组的空间复杂度加上额外变量的空间复杂度,即O(n) + O(1) = O(n)。
五、总结知识点
-
数组的声明与初始化:使用
int[] answer = new int[n];
声明并初始化一个整型数组,长度为n
。 -
循环结构:使用
for
循环来遍历数组,这是控制流结构的一种,用于重复执行一段代码直到指定的条件不再满足。 -
变量赋值与更新:在循环中,变量
left
和right
被初始化并更新,用于计算数组元素的左侧和右侧乘积。 -
数组的索引访问:使用数组索引
answer[i]
和nums[i]
来访问和更新数组中的元素。 -
乘法操作:在循环中,使用乘法
*
操作符来计算连续元素的乘积。 -
算法逻辑:通过两次遍历数组,分别计算每个元素的左侧乘积和右侧乘积,并在第二次遍历中合并这两个乘积得到最终结果。
-
空间优化:尽管题目要求不使用除法,但代码避免了使用额外的空间来存储除当前元素外所有元素的乘积,而是通过在原数组上操作来实现。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。