数组累加和三连2:arr可小于等于大于0,请问累加和为k的子数组最大长度是多少

数组累加和三连2:arr可小于等于大于0,请问累加和为k的子数组最大长度是多少?

提示:之前见过数组累加和的另外三个重要题目,动态规划,可以认为是数组累加和456连
之前数组三连的第1连:
【1】数组累加和三连1:arr全大于0,请问累加和为k的子数组最大长度是多少

重要的数组累加和456连:
【4】数组arr中必须以i位置结尾的子数组,其最大累加和是多少?
【5】数组arr的0–i范围上任选一个子数组的最大累加和是多少?
【6】给定数组arr和k,求3个不重叠的长为k的子数组的累加和之和最大值是多少?

其中,4为5的基础,5又是6的基础,一定学仔细了

今天要讲得典型数组累加和三连的解法

今天讲数组累加和三中最简单的1连,2连


题目

数组累加和三连2:arr可小于等于大于0,请问累加和为k的子数组最大长度是多少?


一、审题

示例:arr=5 -1 4 -3 0 3
k=0
单独0可以,长度为1
-1 4 -3可以,长度为3
但是你看,其实 -1 4 -3 0 要也行啊
长度为4

所以,这跟数组三连1不一样,那边arr>0
这里arr随意,没有单调性了


求取结果没有单调性,考虑以arr的i位置结尾的情况

既然没有单调性,那就考虑累加和以i位置结尾的情况

咱们首先准备一个数组的前缀累加和sum
arr = 5 -1 4 -3 0 3
sum=0 5 4 8 5 5 8
位置=-1 0 1 2 3 4 5
代表0–i位置的累加和
0是最开始累加和为0,对应位置是-1

我们为什么要准备前缀累加和数组?
事情是这样的
想要得到任意范围内L–R的累加和
则只需要sum[R]-sum[L-1]
在这里插入图片描述
这个知识点,我讲过多次了

所以呢,令j=R-1的话,让L–R内累加和为k时
sum[R]-sum[j]=k
故sum[j]=sum[R]-k
咱们寻找此前sum记录的累加和为sum[R]-k的那个j位置,R-j可不就是这个子数组累加和为k的长度吗?
在这里插入图片描述

举个例子:
sum[i]=1000
曾经有一个sum[j]=800
现在k=200
显然R-j必然就是L–R的长度,看下图
因为800+k=1000
在这里插入图片描述

所以呢?
咱们的解题流程:
(0)每一个R从0–N-1,都做一次结尾,找到的最大长度更新在max中
(1)求过的累加和,咱们找个哈希表map,把sum[j],和j存下来,
(2)在后续求sum[R]过程中,去map里面搜索是否有一个sum[j]=sum[R]-k的出现过,有的话,拿到那个j,R-j就是咱们要的L–R的长度,更新给max。

思想很独特,每个i做一次结尾,找到前面曾经出现过的sum-k那个位置,L–R就是长度,更新给max即可

注意map中这个j位置,是最早出现的j,它使得sum[j]=sum[R]-k,
如果后续还要别的j让sum[j]=sum[R]-k,那后面这些j,是不要熬存入map中的
保证i-j最大,j尽可能小,那存入map中的就是最早出现的j,它很小。

    //复习数组三连2
    public static int sumArrk(int [] arr, int k){
        if (arr == null || arr.length == 0) return 0;
        int N = arr.length;
        HashMap<Integer, Integer> map = new HashMap<>();//key:sum,value:j
        //累加和从前往后累加给sum就行,不用单独搞数组了

        //(0)每一个R从0--N-1,都做一次结尾,找到的最大长度更新在max中
        int sum = 0;
        int max = 0;//结果

        map.put(0, -1);//期初sum=0时,出现的位置-1,方便后面R-j求长度
        for (int i = 0; i < N; i++) {//R=i
            sum += arr[i];//每个i都累加
            //(2)在后续求sum[R]过程中,去map里面搜索是否有一个sum[j]=sum[R]-k的出现过,
            // 有的话,拿到那个j,R-j就是咱们要的L--R的长度,更新给max。
            if (map.containsKey(sum - k)) max = Math.max(max, i - map.get(sum - k));

            //(1)求过的累加和,咱们找个哈希表map,把sum[j],和j存下来,
            //sum没加入过map,加一下
            if (!map.containsKey(sum)) map.put(sum, i);//i位置记下
        }

        //每个i都结尾试了一下,max就有结果
        return max;
    }

    public static void test(){
        int[] arr = {5,6,4,-3,0,3};
        int k = 10;
        System.out.println(getMaxLen(arr, k));
        System.out.println(sumArrk(arr, k));
    }

    public static void main(String[] args) {
        test();
    }

注意: map.put(0, -1);最开始,sum=0时,出现的位置-1,方便后面R-j求长度
万一你碰到这种
-3 3 7
k=7
这种
你不能搞7,长度1
因为-3 3 7就是最长长度
但是曾经map应该放0位置为-1
最早出现的0,那个-1位置,才是我们要的j【而且,有过0了,就不能放新的j了】
这样R-j=2-(-1)=3个,这是对的

如果你不放,那等j=1时,sum=0
你找到的R-j=2-1=1,不行的

测试一波!


5
5

如果你map不提前放0的-1位置试试:错误的

    //复习数组三连2
    public static int wrongSumArrk(int [] arr, int k){
        if (arr == null || arr.length == 0) return 0;
        int N = arr.length;
        HashMap<Integer, Integer> map = new HashMap<>();//key:sum,value:j
        //累加和从前往后累加给sum就行,不用单独搞数组了

        //(0)每一个R从0--N-1,都做一次结尾,找到的最大长度更新在max中
        int sum = 0;
        int max = 0;//结果

        //map.put(0, -1);//期初sum=0时,出现的位置-1,方便后面R-j求长度
        for (int i = 0; i < N; i++) {//R=i
            sum += arr[i];//每个i都累加
            //(2)在后续求sum[R]过程中,去map里面搜索是否有一个sum[j]=sum[R]-k的出现过,
            // 有的话,拿到那个j,R-j就是咱们要的L--R的长度,更新给max。
            if (map.containsKey(sum - k)) max = Math.max(max, i - map.get(sum - k));

            //(1)求过的累加和,咱们找个哈希表map,把sum[j],和j存下来,
            //sum没加入过map,加一下
            if (!map.containsKey(sum)) map.put(sum, i);//i位置记下
        }

        //每个i都结尾试了一下,max就有结果
        return max;
    }

    public static void test(){
        int[] arr = {5,6,4,-3,0,3};
        int[] arr2 = {-3,3,7};
        int k = 10;
        int k2 = 7;
        System.out.println(getMaxLen(arr, k));
        System.out.println(sumArrk(arr2, k2));
        System.out.println(wrongSumArrk(arr2, k2));
    }

    public static void main(String[] args) {
        test();
    }

3
1

是吧,不对的


总结

提示:重要经验:

1)数组累加和三连2,当遇到累加和压根和子数组的长度不是单调关系时,考虑子数组以i结尾的情况,这时候就需要用前缀累加和数组了。
2)当哈希表map中有一个最早j位置的sum等于sum[i]-k时,L–R上累加和为k的长度就应该是i-j
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值