hot100_除了自身以外数组的乘积

除了自身以外数组的乘积

hot100_除了自身以外数组的乘积

分析

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除了 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。

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

示例 1:

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

核心思想:

answer[i]=左侧所有元素乘积×右侧所有元素乘积answer[i] = 左侧所有元素乘积 \times 右侧所有元素乘积

ans[i]=(∏k=0i−1nums[k])×(∏k=i+1n−1nums[k])ans[i] = \Bigl(\prod_{k=0}^{i-1} nums[k]\Bigr) \times \Bigl(\prod_{k=i+1}^{n-1} nums[k]\Bigr)


题解

左右辅助数组

开两个数组 L,RL,R。

  • L[i]L[i]:下标 i 左侧全部元素乘积,L[0]=L[0]=
  • R[i]R[i]:下标 i 右侧全部元素乘积,R[n−1]=1R[n-1]=1
  • ans[i]=L[i]∗R[i]ans[i]=L[i] * R[i]
代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> L(n);
        vector<int> R(n);
        vector<int> ans(n);
        L[0] = 1;
        for(int i = 1; i < n; i++) L[i] = L[i - 1] * nums[i - 1];
        R[n - 1] = 1;
        for(int i = n - 2; i >= 0; i--) R[i] = R[i + 1] * nums[i + 1];
        for(int i = 0; i < n; i++) ans[i] = L[i] * R[i];
        return ans;
    }
};

时间复杂度:O(n)O(n),三次线性遍历 空间复杂度:O(n)O(n),L、RL、R 两个辅助数组

两趟遍历空间优化版(面试标准最优解)

复用输出数组 ans 充当左乘积数组;右侧乘积只用一个变量滚动,取消 R 数组。

题目说明:输出返回数组不计入额外空间复杂度。

代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);
        ans[0] = 1;
        for(int i = 1; i < n; ++i){
            ans[i] = ans[i-1] * nums[i-1];
        }
        // right 滚动变量,记录当前位置右侧乘积,代替R数组
        int right = 1;
        for(int i = n - 1; i >= 0; --i){
            ans[i] = ans[i] * right;   // 左乘积 * 右乘积 = 结果
            right = right * nums[i];   // 更新右侧累积乘积
        }
        return ans;
    }
};

时间复杂度:O(n)O(n),两轮遍历 空间复杂度:O(1)O(1),仅常数局部变量

双指针单轮遍历

用 lp 左累积乘积、rp 右累积乘积,左右指针同时向中间收拢,一轮循环完成计算。

代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> ans(nums.size(), 1);
        int left = 0, right = nums.size() - 1;
        int lp = 1, rp = 1;
        while(right >= 0 && left <= nums.size()) {
            ans[right] *= rp;
            ans[left] *= lp;
            lp *= nums[left++];
            rp *= nums[right--];
        }
        return ans;
    }
};

时间复杂度:O(n)O(n),单轮循环 空间复杂度:O(1)O(1)

三种方案对比表

方案时间复杂度额外空间特点面试推荐度
左右辅助数组O(n)O(n)逻辑直观⭐⭐
两趟遍历优化版O(n)O(1)可读性好,标准最优⭐⭐⭐⭐⭐
双指针单轮O(n)O(1)代码简洁,边界易错⭐⭐

238. 除了自身以外数组的乘积 - 力扣(LeetCode)

chengzi