LeetCode - 238. 除自身以外数组的乘积
目录
题目
核心思想
算法步骤
图解示例
时间和空间复杂度
读者可能出现的错误写法
正确的写法
题目
238. 除自身以外数组的乘积 - 力扣(LeetCode)
核心思想
使用双指针,将每个位置的结果分解为两部分的乘积:
- 该位置左侧所有元素的乘积
- 该位置右侧所有元素的乘积
算法步骤
创建结果数组:初始化为全1,因为乘法的单位元是1
vector result(nums.size(), 1);
第一次遍历(从左到右):计算每个位置左侧所有元素的乘积
- 维护一个变量left,表示当前位置左侧所有元素的乘积,初始值为1
对于每个位置i:
- 将left的值乘到result[i]上
- 更新left,乘上当前元素nums[i],为下一个位置做准备
int left = 1; for(int i = 0; i < nums.size(); i++) { result[i] *= left; // 当前位置乘以左侧乘积 left *= nums[i]; // 更新左侧乘积 }
第二次遍历(从右到左):计算每个位置右侧所有元素的乘积,并与之前结果相乘
- 维护一个变量right,表示当前位置右侧所有元素的乘积,初始值为1
- 对于每个位置i(从右往左):
- 将right的值乘到result[i]上
- 更新right,乘上当前元素nums[i],为下一个位置做准备
图解示例
对于数组 [1,2,3,4]:
- 初始化:result = [1,1,1,1]
- 第一次遍历(左→右):
- i=0: left=1, result[0]=1*1=1, left=1*1=1
- i=1: left=1, result[1]=1*1=1, left=1*2=2
- i=2: left=2, result[2]=1*2=2, left=2*3=6
- i=3: left=6, result[3]=1*6=6, left=6*4=24
此时 result = [1,1,2,6]
第二次遍历(右→左):
- i=3: right=1, result[3]=6*1=6, right=1*4=4
- i=2: right=4, result[2]=2*4=8, right=4*3=12
- i=1: right=12, result[1]=1*12=12, right=12*2=24
- i=0: right=24, result[0]=1*24=24, right=24*1=24
最终 result = [24,12,8,6]
时间和空间复杂度
- 时间复杂度:O(n),只需要两次遍历数组
- 空间复杂度:O(1),除了结果数组外,只使用了常数额外空间
int right = 1; for(int i = nums.size()-1; i >= 0; i--) { result[i] *= right; // 当前位置乘以右侧乘积 right *= nums[i]; // 更新右侧乘积 }
读者可能出现的错误写法
class Solution {public: vector productExceptSelf(vector& nums) { vector result(nums.size(),1); int left = 1; for(int i = 0; i0;i++) { right = nums[i]; result = result * right; } return result; }};
left = nums[i]; - 这里是错误的赋值。应该是将当前结果乘以左侧乘积,然后更新左侧乘积。
result = result * left; - 这是错误的语法。不能直接用向量乘以整数,需要对向量的每个元素进行操作。
for(int i = n-1;i>0;i++) - 这里有三个问题:
- 使用了未定义的变量 n(应该是 nums.size())
- 循环条件应该是 i >= 0,否则会漏掉索引0
- 递增运算符应该是 i--,否则会导致无限循环
右侧遍历的逻辑也有问题,不应该直接赋值 right = nums[i]
正确的写法
class Solution {public: vector productExceptSelf(vector& nums) { vector result(nums.size(),1); int left = 1; for(int i = 0; i=0; i--) { result[i] = result[i] * right; right = right*nums[i]; //更新right } return result; }};