累加值为k的最长子数组,累加值不大于k的最长子数组

问题描述1:给定一个数组,值全部是正数,请返回累加值为给定值 k 的最长子数组的长度。
解题思路:
这里写图片描述

一开始 l 和 r 均指向数组第一个数,sum=arr[ 0 ]。
如果k>sum,(不知道为什么写sum小于(<)k就是没法显示后面的,只能这么写了(┬_┬)很醉)那么 r 向右移动,sum加上 arr[ r ]。如果sum>k,那么sum去掉此时 l 的值,并且 l 右移。重复上述过程直至 r 到达数组尾部。
如果sum==k ,那么记录下此时的长度,如果比maxlength大,那么更新maxlength,并且sum减去arr [ l ],l 向右移动。
代码如下:

public static int len1(int[] arr, int k) {
        int len=0;
        int l=0,r=0;
        int sum=arr[0];
        while(r<arr.length){
            if (sum==k) {
                len=Math.max(len, r-l+1);
                sum-=arr[l++];//如果sum等于k,sum的值减去l位置的值,l右移
            }else if (sum<k) {
                r++;
                if (r==arr.length-1) {
                    break;
                }
                sum+=arr[r];
            }else {
                sum-=arr[l++];
            }
        }
        return len;
    }

问题描述2:给定一个数组,值可以为正数负数和0,请返回累加和为给定值 K 的最长子数组长度。
解题思路:
这里写图片描述
假设在长度为n的数组arr中,整个数组的和为sum,要求出以n-1结尾的累加和为K的最长子数组。以上图为例,如果最长子数组是从 m+1 到 n-1 的,那么前面 0 到 m 的和一定是 sum-k ,而且此时 m 位置是数组中第一次出现累加和为 sum-k 的位置。
可以简单证明一下:如果在m前面还有累加和为sum-k的情况的位置 p,那么以n-1结尾累加和为k的最长子数组的长度就不是m-1到n-1,而应该是p+1到n-1,此时子数组长度变长,与之前给的条件不相符。所以此时m位置一定是数组中第一次累加和出现sum-k的位置。
我们可以用一个map来存储数组中第一次出现累加和sum和它对应的index的值。
在每一次查找时,先在map中找是否有存在sum-k的key,(i为出现sum的下标,j为sum-k对应的下标)如果有,那么此时的子数组的长度就是 i-j,如果不存在,就将(sum,i)插入map中。
但是按照上面的方法,可能会将0位置跳过,以下面的数组举例:

value46798
index01234


如果k=4,在index=0时,sum=4,此时在map中查找是否存在key=sum-k=0的情况,但是map中为空,查不到,所以就直接跳过了,没有进行任何处理,maxlength还是0,最后的返回值就是0,可是预期的结果应该是1。所以为了避免这种情况的发生,应该在一开始就讲(0,-1)插入map中。
代码如下:

public static int len2(int[] arr,int k) {
        if(arr==null||k<0||arr.length==0)
            return 0;
        int len=0;
        HashMap<Integer, Integer> map=new HashMap<Integer,Integer>();
        map.put(0, -1);
        int sum=0;
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];//求出到i位置的和
            if (map.containsKey(sum-k)) {
                //如果map中记录了sum-k这个值对应的index
                //更新len的长度
                len=Math.max(i-map.get(sum-k), len);
            }
            if (!map.containsKey(sum)) {
                //如果map中没有记录sum
                //那么就将sum插入map中
                map.put(sum, i);
            }
        }

        return len;
    }

问题描述3:给定一个数组,值可以为正数负数和0,请返回累加和不大于k的最长子数组长度。
解题思路:
分两步来计算:先计算以每个数开头的最小累加和以及他们的右边界,再将累加和累加起来直至大于k。

首先计算以 i 开头往后的累加和,从数组尾部开始,举例如下:

arr_index0123456
arr_value43-267-3-1
min_value41-263-4-1
min_index0223666

其中min_value是最小累加和,min_index是对应的右边界。从右往左遍历数组,如果要求出 i 位置的最小累加和,那么就要看 i+1 位置的最小累加和,如果min_value[ i+1 ]<0,那么min_value[ i ]=arr[ i ]+min_value[ i+1 ],min_index[ i ]=min_index[ i+1 ],否则min_value[ i ]=arr[ i ],min_index[ i ]=i。
接下来再从左往右遍历数组,如果以 i 开头的最小和>k,那么再往后面累加的累加和都>k。
这里写图片描述
上图中的sum1,sum2,sum3,sum4等块中包含至少一个数,从l=0位置开始遍历,如果s+sum1<=k那么s+=sum1,记录下右边界,继续往后遍历,如果s+sum>k,记录下此时s对应的长度。然后 l 右移,此时在s中减去arr[ l ]的值,继续向后遍历,直至结束
参考代码如下:

public static int len3(int[] arr,int k) {
        int len=0;
        int[] sum=new int[arr.length];//以i开头的最小值
        HashMap<Integer, Integer> emap=new HashMap<>();//以i开头的右边界(i,右边界)
        sum[arr.length-1]=arr[arr.length-1];
        emap.put(arr.length-1, arr.length-1);
        for(int i=arr.length-2;i>=0;i--){
            if (sum[i+1]<0) {
                sum[i]=sum[i+1]+arr[i];
                emap.put(i, emap.get(i+1));
            }else{
                sum[i]=arr[i];
                emap.put(i,i);
            }
        }
        int s=0;//最长的和
        int end=0;//右边界
        for(int i=0;i<arr.length-1;i++){
            while(end<arr.length&&s+sum[end]<=k){
                //逐步向后加直至到结尾或者大于k
                s+=sum[end];
                end=emap.get(end)+1;
            }
            //下一轮循环应该从i+1开始,但是不需要重新计算,直接将s中去掉arr[i]即可
            s-=end>i?arr[i]:0;
            len=Math.max(len, end-i);
            end=Math.max(end, i+1);
        }
        return len;
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值