未排序数组累加和为给定值的最长子数组系列问题

题目一:未排序正数数组中累加和为给定值的最长子数组的长度

给定一个数组arr,该数组无序,但每个值均为正数,再给定一个正数k。求arr的所有子数组中所有元素相加和为k的最长子数组的长度

例如,arr = [1, 2, 1, 1, 1], k = 3

累加和为3的最长子数组为[1, 1, 1],所以结果返回3

[要求]

时间复杂度为O(n)O(n),空间复杂度为O(1)O(1)

[示例]

输入

5 3

1 2 1 1 1

输出:3

import java.util.Scanner;
import java.lang.Math;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        //第一行两个整数n,k.n表示数组长度,k表示相加和
        int n=sc.nextInt();
        int k=sc.nextInt();
        //第二行n个整数表示数组内的数
        int[] arr=new int[n];
        for(int i=0;i<arr.length;i++){
            arr[i]=sc.nextInt();
        }
        //使用双指针方法来寻找最长子数组
        //给定两个指针left,right,初始时指向数组第一个元素
        //声明一个变量sum,用于存放子数组arr[left...right]的和
        //声明一个变量len,用于存放最长子数组的长度
        //当sum==k时,表示找到和为k的子数组,此时将len与该子数组长度(right-left+1)比较找出最大值
        //然后将sum减去arr[left],此时sum表示子数组arr[left+1...right]的和
        //当sum<k时,表示可以继续向下查找以left为首的和为k的子数组,即将right++,sum加上arr[right],表示子数组arr[left...right+1]的和
        //当sum>k时,表示以left为首无法找到和为k的子数组,则将left++,sum-=arr[left],表示arr[left+1..right]
        int left=0;
        int right=0;
        int sum=arr[0];
        int len=0;
        while(right<n){
            if(sum==k){
                len=Math.max(len,(right-left+1));
                sum-=arr[left];
                left++;
            }else if(sum<k){
                right++;
                if(right==n){
                    break;
                }
                sum+=arr[right];
            }else if(sum>k){
                sum-=arr[left];
                left++;
            }
        }
        System.out.println(len);
    }
}

题目二:未排序数组中累加和为给定值的最长子数组长度

给定一个无序数组arr, 其中元素可正、可负、可0。给定一个整数k,求arr所有子数组中累加和为k的最长子数组长度

[示例]

输入

5 0

1 -2 1 1 1

输出

3

import java.util.Scanner;
import java.lang.Math;
import java.util.HashMap;
//sum保存遍历过的数组的所有元素的和
//sum[i..k]=arr[i]+arr[i+1]+..+arr[k]
//sum[i..j]=arr[i]+arr[i+1]+..+arr[j]
//sum[i..k]-sum[i..j]=k,即arr[j+1]+arr[j+2]+..+arr[k]=k
//那么sum[i..k]-sum[i..j],即当遍历过的数组中所有元素的值的和为sum-k时,此时sum-k的下标值-当前数组下标值即为满足条件的子数组
//从左到右遍历数组,假设遍历到位置i,记录arr[0..i]的累加和sum,如果该累加和不存在于map中,将该sum作为key,i作为value添加到map中;
//否则不进行添加。接下来判断sum-k这个累加和是否存在于map中,如果存在的话,假设位置记为index,那么可想而知,i-index其实就是一个累加和
//为k的子数组长度。使用一个全局变量保存遍历到的最长子数组长度。继续遍历数组按照上述方法寻找其他的累加和为k的子数组
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        //第一行两个整数n,k.n表示数组长度,k表示相加和
        int n=sc.nextInt();
        int k=sc.nextInt();
        //第二行n个整数表示数组内的数
        int[] arr=new int[n];
        for(int i=0;i<arr.length;i++){
            arr[i]=sc.nextInt();
        }
        
        HashMap<Integer,Integer> map=new HashMap<>();
        int sum=0;
        int len=0;
        map.put(0, -1);//预处理将(0,-1)放入到hashmap中,目的是可以求出从0位置开始的子数组
		for(int i=0;i<arr.length;i++){
			//sum保存遍历过的数组的所有元素的和
			sum+=arr[i];
			if(!map.containsKey(sum)){
				//如果hashmap中没有保存sum的值,则将其保存进去
				map.put(sum, i);
			}
			if(map.containsKey(sum-k)){
				//如果hashmap中保存了sum-k的值,则此时其value值到当前数组下标i的范围为所求范围
				int j=map.get(sum-k);
				len=Math.max(len, i-j);
			}
		}
		System.out.println(len);
    }
}

题目三:未排序数组中累加和为给定值的最长子数组系列问题补1

给定一个无序数组arr,其中元素可正、可负、可0。求arr所有子数组中正数与负数个数相等的最长子数组的长度。

[要求]

时间复杂度为O(n)O(n),空间复杂度为O(n)O(n)

[示例]

输入:

5

1 -2 1 1 1

输出:

2

import java.util.Scanner;
import java.util.HashMap;
import java.lang.Math;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        //第一行一个整数n,表示数组长度
        int n=sc.nextInt();
        //接下来一行有n个数表示数组中的数
        int[] arr=new int[n];
        for(int i=0;i<arr.length;i++){
            arr[i]=sc.nextInt();
        }
        //将正数转为1,负数转为-1
        for(int i=0;i<arr.length;i++){
            if(arr[i]>0){
                arr[i]=1;
            }else if(arr[i]<0){
                arr[i]=-1;
            }
        }
        //如果和为0,则说明正数和负数的个数相等
        int k=0;
        int sum=0;
        int len=0;
        HashMap<Integer,Integer> map=new HashMap<>();
        map.put(0,-1);
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];
            if(!map.containsKey(sum)){
                map.put(sum,i);
            }
            if(map.containsKey(sum-k)){
                int j=map.get(sum-k);
                len=Math.max(len,i-j);
            }
        }
        System.out.println(len);
    }
}

题目四:未排序数组中累加和为给定值的最长子数组系列问题补2

给定一个无序数组arr,其中元素只能是1或0。求arr所有的子数组中0和1个数相等的最长子数组的长度 

[要求]

时间复杂度为O(n)O(n),空间复杂度为O(n)O(n)

[示例]

输入

5

1 0 1 0 1

输出

4

import java.util.Scanner;
import java.util.HashMap;
import java.lang.Math;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        //第一行一个整数n,表示数组长度
        int n=sc.nextInt();
        //接下来一行有n个数表示数组中的数
        int[] arr=new int[n];
        for(int i=0;i<arr.length;i++){
            arr[i]=sc.nextInt();
        }
        //将0转换为-1。这样当和为0时数组中的0和1的个数相等
        for(int i=0;i<arr.length;i++){
            if(arr[i]==0){
                arr[i]=-1;
            }
        }
        //k=0;
        HashMap<Integer,Integer> map=new HashMap<>();
        map.put(0,-1);
        int sum=0;
        int len=0;
        int k=0;
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];
            if(!map.containsKey(sum)){
                map.put(sum,i);
            }
            if(map.containsKey(sum-k)){
                int j=map.get(sum-k);
                len=Math.max(len,i-j);
            }
        }
        System.out.println(len);
    }
}

题目五:未排序数组中累加和小于或等于给定值的最长子数组长度

给定一个无序数组arr,其中元素可正、可负、可0。给定一个整数k,求arr所有的子数组中累加和小于或等于k的最长子数组长度

例如:arr = [3, -2, -4, 0, 6], k = -2. 相加和小于等于-2的最长子数组为{3, -2, -4, 0},所以结果返回4

[要求]

时间复杂度为O(n)O(n),空间复杂度为O(n)O(n)

[示例]

输入:

5 -2

3 -2 -4 0 6

输出:

4

import java.util.Scanner;
import java.lang.Math;
//求以每个位置结尾的情况下累加和小于等于k,找到全局最大
//sum(j)表示0~j范围内的子数组的累加和
//解题点为找到累加和为>=sum(j)-k的最早出现的位置
//使用辅助数组help存放0~i范围内的累加和的最大值
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int k=sc.nextInt();
        int[] arr=new int[n];
        for(int i=0;i<arr.length;i++){
            arr[i]=sc.nextInt();
        }
        System.out.println(getLessEqualLen(arr,k));
    }
    public static int getLessEqualLen(int[] arr,int k){
        int[] help=new int[arr.length+1];
        int sum=0;
        help[0]=sum;
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];
            help[i+1]=Math.max(sum,help[i]);
        }
        sum=0;
        int len=0;
        int res=0;
        for(int i=0;i<arr.length;i++){
            sum+=arr[i];
            //二分查找
            int start=0;
            int end=help.length-1;
            int index=-1;//最早出现sum-k的下标
            while(start<=end){
                int middle=(start+end)/2;
                if(help[middle]>=sum-k){
                    index=middle;
                    end=middle-1;
                }else{
                    start=middle+1;
                }
            }
            //二分查找结束
            len=index==-1?0:i-index+1;
            res=Math.max(res,len);
        }
        return res;
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值