leetcode 560—— 前缀和技巧

前缀和

1、定义

前缀和的思路是这样的,对于一个给定的数组 nums,我们额外开辟一个前缀和数组进行预处理。

int n = nums.size();
// 前缀和数组
vector<int>preSum(n + 1);
for (int i = 0; i < n; i++)
    preSum[i + 1] = preSum[i] + nums[i];

在这里插入图片描述
preSum[i] 就是 nums[0..i-1] 的和。那么如果我们想求 nums[i..j] 的和,只需要一步操作 preSum[j+1]-preSum[i] 即可,而不需要重新去遍历数组了。

2、题解:LeetCode560. 和为K的子数组

原题
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

说明 :

  • 数组的长度为 [1, 20,000]。
  • 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

方法一:穷举所有子数组,计算子数组的和

关键是,如何快速得到某个子数组的和呢,比如说给你一个数组 nums,让你实现一个接口 sum(i, j),这个接口要返回 nums[i..j] 的和,而且会被多次调用,你怎么实现这个接口呢?

因为接口要被多次调用,显然不能每次都去遍历 nums[i..j],有没有一种快速的方法在 O(1) 时间内算出 nums[i..j] 呢?这就需要前缀和技巧了。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int len = nums.size();
        vector<int> arrsum(len + 1);
        for(int i = 0;i < len;i++)
        {
            arrsum[i+1] = arrsum[i] + nums[i];
        }
        int ans = 0;
        for(int i = 1;i <= len;i++)
        {
            for(int j = 0; j < i;j++)
            {
                if(arrsum[i] - arrsum[j] == k)
                    ans++;
            }
        }
        return ans;
    }
};

问题:时间复杂度太高:超时
在这里插入图片描述

优化解法

for(int i = 1;i <= len;i++)
        {
            for(int j = 0; j < i;j++)
            {
                if(arrsum[i] - arrsum[j] == k)
                    ans++;
            }
        }

第二层 for 循环在干嘛呢?

  • 有几个 j 能够使得 sum[i]sum[j]差为 k。毎找到一个这样的 j,就把结果加一。
arrsum[i] - arrsum[j] == k
arrsum[i] == arrsum[j] - k

优化的思路是:
直接记录下有几个 sum[j]sum[i] - k 相等,直接更新结果,就避免了内层的 for 循环。可以用哈希表,在记录前缀和的同时记录该前缀和出现的次数

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int sum = 0, ans = 0;
        unordered_map<int,int> mp;
        mp[0] = 1;
        for(int i: nums){
            sum += i;
            if(mp.find(sum-k) != mp.end()) 
                ans += mp[sum-k];
            //把前缀和 nums[0..i] 加入并记录出现次数
            mp[sum] ++;
        }
        return ans;
    }
};

比如说下面这个情况,需要前缀和 8 就能找到和为 k 的子数组了,之前的暴力解法需要遍历数组去数有几个 8,而优化解法借助哈希表可以直接得知有几个前缀和为 8。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/JMW1407/article/details/107655721