前缀和求解连续子数组问题(LeetCode 560.和为k的子数组,974.和可被k整除的子数组,525.连续数组)

LeetCode 560.和为k的子数组

图为原题目,上方文字为原题链接。

题意分析:

需要注意的就是这里的子数组的连续的,题意就是要在数组中找一段连续的子数组使其和为k。

题目解析:

方法1:暴力方法

我们很容易想到暴力方法来解决这个问题,分别定义两个变量来表示子数组的起始和结束位置,然后求和判断是否等于k。

方法2:前缀和

利用前缀和有意想不到的效果。

给定数组中从开始到中间的某一段即图中到 i 的位置,然后计算x部分,x是从0下标位置开始的前缀和,当x = sum - k 时,即找到一个满足条件的解。

    int subarraySum(vector<int>& nums, int k) {
        int sum = 0;//表示当前前缀和
        int ret = 0;//表示最终结果
        unordered_map<int,int> hash;//建立前缀和和对应数量的哈希表
        hash[0]=1;
        for(int i = 0;i<nums.size();i++)
        { 
            sum+=nums[i];//当前前缀和
            if(hash.count(sum-k)) ret+=hash[sum-k];
            //如果在当前之前有前缀和为sum-k的情况,说明之后到i的位置就是和为k的情况
            hash[sum]++;//将当前前缀和保存到哈希表中
        }
        return ret;
    }

hash表建立的是前缀和与出现次数的对应关系,当前缀和满足条件时,他出现的次数就是满足题意的次数。hash[0]=1,表示前缀和为0的时候默认有一种情况,就是一个元素都没有的时候。

sum代表的是当前的前缀和,当某个前缀和满足sum-k时,说明从满足条件时的后一个下标开始到当前 i 位置下标结束是满足条件的情况。

前缀和之所以用sum来表示而不是用数组表示是因为,之前的每次不同sum-k的情况都会保存到hash中,因而只需要一个整形来表示当前前缀和即可。

通过前缀和+哈希表的方法,将直接找子数组的情况转化为了找前子数组和的情况,相当巧妙。

974.和可被k整除的子数组

LeetCode,974.和可被k整除的子数组

图为原题目,上方文字为原题链接。

题意分析:

找连续的子数组的和能被k整除。读原题的话有一个细节就是数组中元素有负值。

题目解析:

方法1:暴力方法

套循环挨个计算然后判断。

方法2:前缀和

此题与上道题相似都是找满足条件的连续子数组,区别在于条件不同。所以可以参照上题的思路来解决此题。

    int subarraysDivByK(vector<int>& nums, int k)
    {
    int ret = 0;//存最终结果
    int sum = 0;//记录当前前缀和
    unordered_map<int,int>hash;//建立余数与出现次数的关系
    hash[0]=1;//代表一个元素也不包含的那个情况,余数为0
    for(int i = 0;i<nums.size();i++)
    {
        sum+=nums[i];//当前前缀和
        int r = (sum%k+k)%k;//当前前缀和余数,这个余数也是之后的判断条件
        if(hash.count(r)) ret=ret+hash[r];
        //如果哈希表保存的之前的余数满足与r相同,则说明满足条件
        hash[r]++;
        //将当前余数保存到哈希表中,便于后续使用
    }
    return ret;
    }

思路与第一道题完全类似,区别在于判断条件不同,为什么能这么判断是用到了同余定理,如果(a-b)%k=0,则能说明 a%k=b%k。

525.连续数组

LeetCode,525.连续数组

图为原题目,上方文字为原题链接。

题意分析:

寻找包含相同数量0和1的最长连续子数组。

题目解析:

    int findMaxLength(vector<int>& nums)
    {
    unordered_map<int,int>hash;//前缀和与下标的对应关系
    int sum = 0;//记录当前前缀和
    int ret = 0;//结果
    hash[0]=-1;
    //因为hash对应的是与下标的关系,所以一个元素都没有的时候,对应下标为-1
    for(int i = 0;i<nums.size();i++)
    {
        if(nums[i]==0)
            sum+=-1;
        else sum+=nums[i];
        //sum记录的是前缀和,题目要求的是相同数量的0和-1,所以在统计的时候将0换为-1更方便统计,和为0
        if(hash.count(sum)) ret=max(ret,i-hash[sum]);
        //因为题目要求是最长,所以这里语句为记录最长
        else hash[sum]=i;
    }
    return ret;
    }

此题目在思想上与前两道题目相同,区别在于最后求得是最长,所以hash里面对应关系为前缀和与下标,将0换为-1是为了方便判断,1与0数量相同时sum和刚好为0。

if里面的判断是基于hash保存的之前的前缀和的数据,看图中,如果sum值与hash中能找到对应值,则说明中间区段子数组和为0,就是满足条件的。在判断完成之后也无需更新hash的值因为是求最长,所以最早出现的那个下标最小,则满足条件时计算出来的更长。

这三道题目都采用了前缀和的方法,根据不同的题目要求都将求子数组的问题转化成立求前缀和的问题,所以在以后遇到求连续子数组的时候可以逆向,通过卡结束位置求前缀和再结合哈希表来求解。

本次分享到此结束,欢迎大家批评指正!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值