题目一:未排序正数数组中累加和为给定值的最长子数组的长度
给定一个数组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;
}
}