# 238. 除自身以外数组的乘积 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解
访问原文链接:[238. 除自身以外数组的乘积 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解](https://leetcode.blog/zh/leetcode/238-product-of-array-except-self),体验更佳!
力扣链接:[238. 除自身以外数组的乘积](https://leetcode.cn/problems/product-of-array-except-self), 难度等级:**中等**。
## LeetCode “238. 除自身以外数组的乘积”问题描述
给你一个整数数组 `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`
- 输入 **保证** 数组 `answer[i]` 在 **32 位** 整数范围内
**进阶**:你可以在 `O(1)` 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 **不被视为** 额外空间。)
### [Hints]
提示 1
Think how you can efficiently utilize prefix and suffix products to calculate the product of all elements except self for each index. Can you pre-compute the prefix and suffix products in linear time to avoid redundant calculations?
提示 2
Can you minimize additional space usage by reusing memory or modifying the input array to store intermediate results?
## 思路 1
1. **分解问题**:将"除自身外的乘积"分解为"左侧乘积 × 右侧乘积"
2. **两次遍历**:先计算每个元素的左侧乘积,再计算右侧乘积
3. **合并结果**:将左右乘积相乘得到最终结果
## “预计算技术”的模式
**预计算技术(Pre-Computation Techniques)** 是一种通过提前计算并存储中间结果或常用数据,以减少实时计算开销的优化方法。其核心思想是**“用空间换时间”**。
#### 主要应用场景
- **数组的前缀/后缀**计算问题。
- **高频计算问题**:如斐波那契数列、阶乘、素数表等,通过预先生成查找表(Lookup Table)避免重复计算。
- **动态规划(DP)**:预先计算子问题的解并存储,如背包问题、最短路径问题。
## 步骤
1. **初始化数组**:
- 创建`leftProducts`数组,存储每个元素左侧所有元素的乘积
- 创建`rightProducts`数组,存储每个元素右侧所有元素的乘积
2. **计算左侧乘积**(从左到右遍历):
- 第一个元素左侧乘积初始化为1
- 后续元素:`leftProducts[i] = nums[i-1] * leftProducts[i-1]`
3. **计算右侧乘积**(从右到左遍历):
- 最后一个元素右侧乘积初始化为1
- 前序元素:`rightProducts[i] = nums[i+1] * rightProducts[i+1]`
4. **合并结果**:
- 遍历每个位置,将左右乘积相乘:`answer[i] = leftProducts[i] * rightProducts[i]`
5. **返回结果数组**
## 复杂度
- 时间复杂度: `O(N)`.
- 空间复杂度: `O(N)`.
## Ruby
```ruby
# @param {Integer[]} nums
# @return {Integer[]}
def product_except_self(nums)
# nums: [1, 2, 3, 4]
# left_products: [1, 1, 2, 6]
# right_products: [24,12, 4, 1]
# answer: [24,12, 8, 6]
size = nums.size
left_products = Array.new(size, 1)
(1...size).each do |i|
left_products[i] = nums[i - 1] * left_products[i - 1]
end
right_products = Array.new(size, 1)
(size - 2).downto(0) do |i|
right_products[i] = nums[i + 1] * right_products[i + 1]
end
answer = []
(0...size).each do |i|
answer << left_products[i] * right_products[i]
end
answer
end
```
## Python
```python
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
size = len(nums)
# nums: [1, 2, 3, 4]
# left_products: [1, 1, 2, 6]
# right_products: [24,12, 4, 1]
# answer: [24,12, 8, 6]
left_products = [1] * size
for i in range(1, size):
left_products[i] = nums[i - 1] * left_products[i - 1]
right_products = [1] * size
for i in range(size - 2, -1, -1):
right_products[i] = nums[i + 1] * right_products[i + 1]
answer = []
for i in range(size):
answer.append(left_products[i] * right_products[i])
return answer
```
## Java
```java
class Solution {
public int[] productExceptSelf(int[] nums) {
int size = nums.length;
// nums: [1, 2, 3, 4]
// left_products: [1, 1, 2, 6]
// right_products: [24,12, 4, 1]
// answer: [24,12, 8, 6]
int[] leftProducts = new int[size];
leftProducts[0] = 1;
for (int i = 1; i < size; i++) {
leftProducts[i] = nums[i - 1] * leftProducts[i - 1];
}
int[] rightProducts = new int[size];
rightProducts[size - 1] = 1;
for (int i = size - 2; i >= 0; i--) {
rightProducts[i] = nums[i + 1] * rightProducts[i + 1];
}
int[] answer = new int[size];
for (int i = 0; i < size; i++) {
answer[i] = leftProducts[i] * rightProducts[i];
}
return answer;
}
}
```
## C++
```cpp
class Solution {
public:
vector productExceptSelf(vector& nums) {
int size = nums.size();
// nums: [1, 2, 3, 4]
// left_products: [1, 1, 2, 6]
// right_products: [24,12, 4, 1]
// answer: [24,12, 8, 6]
vector left_products(size, 1);
for (int i = 1; i < size; i++) {
left_products[i] = nums[i - 1] * left_products[i - 1];
}
vector right_products(size, 1);
for (int i = size - 2; i >= 0; i--) {
right_products[i] = nums[i + 1] * right_products[i + 1];
}
vector answer(size);
for (int i = 0; i < size; i++) {
answer[i] = left_products[i] * right_products[i];
}
return answer;
}
};
```
## JavaScript
```javascript
/**
* @param {number[]} nums
* @return {number[]}
*/
var productExceptSelf = function(nums) {
const size = nums.length
const leftProducts = new Array(size).fill(1)
for (let i = 1; i < size; i++) {
leftProducts[i] = nums[i - 1] * leftProducts[i - 1]
}
const rightProducts = new Array(size).fill(1)
for (let i = size - 2; i >= 0; i--) {
rightProducts[i] = nums[i + 1] * rightProducts[i + 1]
}
const answer = []
for (let i = 0; i < size; i++) {
answer.push(leftProducts[i] * rightProducts[i])
}
return answer
};
```
## C#
```csharp
public class Solution
{
public int[] ProductExceptSelf(int[] nums)
{
int size = nums.Length;
// nums: [1, 2, 3, 4]
// left_products: [1, 1, 2, 6]
// right_products: [24,12, 4, 1]
// answer: [24,12, 8, 6]
int[] leftProducts = new int[size];
leftProducts[0] = 1;
for (int i = 1; i < size; i++)
leftProducts[i] = nums[i - 1] * leftProducts[i - 1];
int[] rightProducts = new int[size];
rightProducts[size - 1] = 1;
for (int i = size - 2; i >= 0; i--)
rightProducts[i] = nums[i + 1] * rightProducts[i + 1];
int[] answer = new int[size];
for (int i = 0; i < size; i++)
answer[i] = leftProducts[i] * rightProducts[i];
return answer;
}
}
```
## Go
```go
func productExceptSelf(nums []int) []int {
size := len(nums)
leftProducts := make([]int, size)
leftProducts[0] = 1
for i := 1; i < size; i++ {
leftProducts[i] = nums[i - 1] * leftProducts[i - 1]
}
rightProducts := make([]int, size)
rightProducts[size - 1] = 1
for i := size - 2; i >= 0; i-- {
rightProducts[i] = nums[i + 1] * rightProducts[i + 1]
}
answer := make([]int, size)
for i := 0; i < size; i++ {
answer[i] = leftProducts[i] * rightProducts[i]
}
return answer
}
```
## Other languages
```java
// Welcome to create a PR to complete the code of this language, thanks!
```
## 思路 2
1. **空间优化**:利用输出数组存储中间结果,避免额外空间
2. **两阶段计算**:先计算左侧乘积存入结果,再动态计算右侧乘积并直接合并
3. **原地操作**:仅使用一个变量动态维护右侧乘积
## 步骤
1. **初始化结果数组**:
- 创建大小相同的`answer`数组,初始值全1
2. **计算左侧乘积**(左→右遍历):
- 初始化`left_product = 1`
- 遍历时:`answer[i] = left_product`,然后`left_product *= nums[i]`
3. **计算右侧乘积并合并**(右→左遍历):
- 初始化`right_product = 1`
- 遍历时:`answer[i] *= right_product`,然后`right_product *= nums[i]`
4. **返回结果数组**
- 空间复杂度:O(1)(输出数组不计入空间复杂度)
## 复杂度
- 时间复杂度: `O(N)`.
- 空间复杂度: `O(1)`.
## Ruby
```ruby
# @param {Integer[]} nums
# @return {Integer[]}
def product_except_self(nums)
size = nums.size
answer = Array.new(size, 1)
left_product = 1
(0...size).each do |i|
answer[i] = left_product
left_product *= nums[i]
end
# nums: [1, 2, 3, 4]
# answer: [1, 1, 2, 6] left_product done
# answer: [24,12, 8, 6] right_product done
right_product = 1
(size - 1).downto(0) do |i|
answer[i] *= right_product
right_product *= nums[i]
end
answer
end
```
## Python
```python
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
size = len(nums)
answer = [1] * size
left_product = 1
for i in range(size):
answer[i] = left_product
left_product *= nums[i]
# nums: [1, 2, 3, 4]
# answer: [1, 1, 2, 6] left_product done
# answer: [24,12, 8, 6] right_product done
right_product = 1
for i in range(size-1, -1, -1):
answer[i] *= right_product
right_product *= nums[i]
return answer
```
## Java
```java
class Solution {
public int[] productExceptSelf(int[] nums) {
int size = nums.length;
int[] answer = new int[size];
Arrays.fill(answer, 1);
int leftProduct = 1;
for (int i = 0; i < size; i++) {
answer[i] = leftProduct;
leftProduct *= nums[i];
}
// nums: [1, 2, 3, 4]
// answer: [1, 1, 2, 6] leftProduct done
// answer: [24,12, 8, 6] rightProduct done
int rightProduct = 1;
for (int i = size - 1; i >= 0; i--) {
answer[i] *= rightProduct;
rightProduct *= nums[i];
}
return answer;
}
}
```
## C++
```cpp
class Solution {
public:
vector productExceptSelf(vector& nums) {
int size = nums.size();
vector answer(size, 1);
int left_product = 1;
for (int i = 0; i < size; ++i) {
answer[i] = left_product;
left_product *= nums[i];
}
// nums: [1, 2, 3, 4]
// answer: [1, 1, 2, 6] left_product done
// answer: [24,12, 8, 6] right_product done
int right_product = 1;
for (int i = size - 1; i >= 0; --i) {
answer[i] *= right_product;
right_product *= nums[i];
}
return answer;
}
};
```
## JavaScript
```javascript
/**
* @param {number[]} nums
* @return {number[]}
*/
var productExceptSelf = function(nums) {
const answer = [];
let left_product = 1;
for (let i = 0; i < nums.length; i++) {
answer[i] = left_product;
left_product *= nums[i];
}
// nums: [1, 2, 3, 4]
// answer: [1, 1, 2, 6] left_product done
// answer: [24,12, 8, 6] right_product done
right_product = 1;
for (let i = nums.length - 1; i >= 0; i--) {
answer[i] *= right_product;
right_product *= nums[i];
}
return answer;
};
```
## C#
```csharp
public class Solution
{
public int[] ProductExceptSelf(int[] nums)
{
int size = nums.Length;
int[] answer = new int[size];
Array.Fill(answer, 1);
int leftProduct = 1;
for (int i = 0; i < size; i++)
{
answer[i] = leftProduct;
leftProduct *= nums[i];
}
// nums: [1, 2, 3, 4]
// answer: [1, 1, 2, 6] leftProduct done
// answer: [24,12, 8, 6] rightProduct done
int rightProduct = 1;
for (int i = size - 1; i >= 0; i--)
{
answer[i] *= rightProduct;
rightProduct *= nums[i];
}
return answer;
}
}
```
## Go
```go
func productExceptSelf(nums []int) []int {
n := len(nums)
answer := make([]int, n)
answer[0] = 1
for i := 1; i < n; i++ {
answer[i] = answer[i-1] * nums[i-1]
}
right := 1
for i := n - 1; i >= 0; i-- {
answer[i] *= right
right *= nums[i]
}
return answer
}
```
## Other languages
```java
// Welcome to create a PR to complete the code of this language, thanks!
```
亲爱的力扣人,为了您更好的刷题体验,请访问 [LeetCode.blog](https://leetcode.blog/zh)。
本站敢称力扣题解最佳实践,终将省你大量刷题时间!
原文链接:[238. 除自身以外数组的乘积 - LeetCode Python/Java/C++/JS/C#/Go/Ruby 题解](https://leetcode.blog/zh/leetcode/238-product-of-array-except-self).
GitHub 仓库: [leetcode-python-java](https://github.com/leetcode-python-java/leetcode-python-java).