前缀和——238. 除自身以外数组的乘积

前缀和——238. 除自身以外数组的乘积_第1张图片

文章目录

    • 1. 题目
    • 2. 算法原理
      • 解法一:暴力求解
      • 解法二:前缀和(积)
    • 3. 代码实现

1. 题目

题目链接:238. 除自身以外数组的乘积 - 力扣(LeetCode)

给你一个整数数组 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 <= 105
  • -30 <= nums[i] <= 30
  • 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内

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

2. 算法原理

本题的意思是,要求出除了当前下标位置,其他位置的乘积

前缀和——238. 除自身以外数组的乘积_第2张图片

解法一:暴力求解

暴力求解就是遍历整个数组,然后再遍历一次除了当前位置的其他位置元素乘积,整个的时间复杂度为O(n2)

解法二:前缀和(积)

这里求除了当前位置的整个数组的乘积,可以理解为求前面一部的前缀积+后面一部分的后缀积

前缀和——238. 除自身以外数组的乘积_第3张图片

预处理:

  • f表示前缀积,f[i]表示[0,i-1]区间内所有元素的乘积f[i] = f[i-1] * nums[i-1]
  • g表示后缀积,g[i]表示[i+1,n-1]区间内所有元素的乘积g[i] = g[i+1] * nums[i+1]

这里因为f[i]的区间是[0,i-1],所以这里的i,实际上是i-1
前缀和——238. 除自身以外数组的乘积_第4张图片

使用预处理之后的数组:

有了预处理的数组,我们只需让f[i]*g[i]即可求出当前位置的值

细节问题:

这里因为要求的是乘积,所以f[0]这里要提前处理一下,f[0] = 1;同理g[n-1] = 1
f是从前往后,g是从后往前

这个时间复杂度为O(n)+O(n)+O(n),可理解为O(n)

这里进阶是空间复杂度为O(1)求解,采用的也是前缀积和后缀积,用ret先装前缀积,然后从后往前乘以后缀积。
我们前面采用2个数组装前缀积和后缀积,可以理解得更清晰一些。
前缀和——238. 除自身以外数组的乘积_第5张图片

3. 代码实现

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums)
    {
        int n = nums.size();
        vector<int> f(n) , g(n);
        
        //预处理前缀积
        f[0] = 1;
        g[n-1] = 1;
        for(int i=1;i<n;i++)
            f[i] = f[i-1] * nums[i-1];
        //预处理后缀积
        for(int i=n-2;i>=0;i--)
            g[i] = g[i+1] * nums[i+1];
        
        vector<int> ret(n);
        for(int i=0;i<n;i++)
            ret[i]=f[i]*g[i];
        return ret;
    }
};

优化空间复杂度:

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums)
    {
        int n = nums.size();
        vector<int> ret(n,1);

        int left = 1;
        for(int i=1;i<n;i++)
        {
            left *= nums[i-1];
            ret[i] = left;
        }

        int right = 1;
        for(int i=n-2;i>=0;i--)
        {
            right*=nums[i+1];
            ret[i]*=right;
        }
        return ret;
    }
};

你可能感兴趣的:(原创,刷题,算法,力扣,前缀和,c++)