有关子数组最大累加和的算法小结

 1.求两个子数组最大的累加和
给定一个数组,其中有很多子数组,在所有两个子数组的组合中,找到相加和最大的一组,要求两个子数组无重合部分,最后返回累加和
要求时间复杂度O(N)
算法原型:求一个数组中子数组的最大累加和
思路:设置一个res记录最大的和的值,一个cur记录当前和的值,当res小于cur时,将res替换为cur,否则cur继续累加下一个数,当cur值为负时,将cur置为0,继续累加下一个数
即例如一个数组是[3,4,1,-1,-3,-5,2,8,-6],则
cur:3,7,8,7,4,0,2,10,4
res:3,7,8,8,8,8,8,10,10
即该数组的子数组最大和为10
该算法原型代码为:
public static int maxSum(int[] arr){
	if(arr == null || arr.length == 0){
		return 0;
	}
	int res = arr[0];
	int cur = arr[0];
	for(int i = 1; i< arr.length; i++){
		cur = cur < 0 ? 0 : cur;
		cur += arr[i];
		res = Math.max(res, cur);
	}
	return res;
}

本题是求两个不重合的子数组的最大累加和,思路是将数组中每个值设置为一个分割点,左边为一个子数组,右边为一个子数组,分别求其左右两边子数组的最大累加和,遍历数组中的每一个数即可求出结果。
为了实现时间复杂度为O(N)的要求,可以设置两个辅助数组,分别记录每个数的左右两边的子数组的最大累加和,之后即可直接从两个辅助数组中求出两个不重合的子数组最大累加和
也可以直接设置一个记录右边子数组的最大累加和的子数组,因为是从左向右遍历,所以可以直接省去记录左边的辅助数组。
如上述示例数组的右辅助数组为:[10,10,10,10,10,10,8,-6,0]
本题的示例代码为:
public static int maxSum(int[] arr){
	if(arr == null || arr.length == 0){
		return 0;
	}
	int[] h = new int[arr.length];
	h[arr.length - 1] = arr[arr.length - 1];
	int cur = arr[arr.length - 1];
	for(int i = arr.length - 2; i >= 0; i--){
		cur = cur < 0 ? 0 : cur;
		cur += arr[i];
		h[i] = Math.max(h[i+1], cur);
	}
	int res = arr[0] + h[1];
	int lmax = arr[0];
	cur = arr[0];
	for(int i = 1; i<arr.length - 1; i++){
		cur = cur < 0 ? 0 : cur;
		cur += arr[i];
		lmax = Math.max(lmax, cur);
		res = Math.max(res, lmax + h[i+1]);
	}
	return res;
}

2.未排序正数数组中累加和为给定值的最长子数组长度
给定一个数组arr,该数组无序,但每个值均为正数,在给定一个正数k,求arr的所有子数组中所有元素相加和为k的最长子数组的长度
例如,arr=[1,2,1,1,1], k = 3
则累加和为3的最长子数组为[1,1,1],结果返回为3
要求时间复杂度O(N),额外空间复杂度O(1)
思路:建立两个指针L和R,两个全局变量sum和len,若sum<=k,R++,否则L++,当sum==k时记录len

代码:
public static int getMaxLength(int[] arr, int k){
	if(arr == null || arr.length == 0 || k <= 0){
		return 0;
	}
	int left = 0;
	int right = 0;
	int sum  = arr[0];
	int len = 0;
	while(right < arr.length){
		if(sum == k){
			len = Math.max(len, right-left+1);
			sum -= arr[left++];
		}else if(sum < k){
			right++;
			if(right == arr.length){
				break;
			}
			sum += arr[right];
		}else{
			sum -= arr[left++];
		}
	}
	return len;
}

3.未排序数组中累加和为给定值的最长子数组系列问题 
给定一个无序数组arr,其中元素可正、可负、可0,给定一个整数k,求arr所有的子数组中累加和为k的最长子数组长度。
思路:建立一个辅助数组sum[],sum[i]表示arr数组中前i项之和,若i<j,且sum[i] + k = sum[j],则k等于arr数组中第i+1项到第j项之和。即若必须以第j项为结尾的累加和为k的最长子数组。
public static int maxLength(int[] arr, int k){
	if(arr == null || arr.length == 0){
		return 0;
	}
	HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
	map.put(0,-1); // 这里很重要,如果没有这条规定,则所有从头开始累积的数组失效
	int len = 0;
	int sum = 0;
	for(int i = 0; i<arr.length;i++){
		sum += arr[i];
		if(map.containsKey(sum - k)){
			len = Math.max(i - map.get(sum - k), len);
		}
		if(!map.containsKey(sum)){
			map.put(sum,i);
		}
	}
	return len;
}
给定一个无序数组arr,其中元素可正可负可0,求arr所有的子数组中正数与负数个数相等的最长子数组长度。
相当于:把数组中所有正数置为1,所有负数置为-1,求累加和为0的最长子数组


给定一个无序数组arr,其中元素只能是1或是0,求arr所有的子数组中0和1个数相等的最长子数组长度。
要求时间复杂度为O(N)
相当于:将数组中所有0重置为-1,求累加和为0的最长子数组

4.未排序数组中累加和小于等于给定值的最长子数组长度
给定一个无序数组arr,给定一个整数k,求arr所有的子数组中累加和小于等于k的最长子数组长度
例如:arr = {3,-2,-4,0,6}, k=-2,相加和小于或等于-2的最长子数组为{3,-2,-4,0},所以结果返回4.要求时间复杂度为O(N*logN)
arr[] = {3,2,-3,2,-2,6,-3,1}
sum[] = {3,5,2,4,2,8,5,6}
h[] = {3,5,5,5,5,8,8,8}

public static int maxLength(int[] arr, int k){
	int[] h = new int[arr.length + 1];
	int sum = 0;
	h[0] = sum;
	for(int i = 0; i != arr.length; i++){
		sum += arr[i];
		h[i+1] = Math.max(sum,h[i]);
	}
	sum = 0;
	int res = 0;
	int pre = 0;
	int len = 0;
	for(int i = 0; i!=arr.length;i++){
		sum += arr[i];
		pre = getLessIndex(h, sum - k);
		len = pre == -1 ? 0 : i - pre + 1;
		res = Math.max(res, len);
	}
	return res;
}
public static int getLessIndex(int[] arr, int num){
	//在辅助数组中找到第一个大于等于h的位置
	int low = 0;
	int high = arr.length - 1;
	int mid = 0;
	int res = -1;
	while(low <= high){
		mid = (low + high) / 2;
		if(arr[mid] >= num){
			res = mid;
			high = mid -1;
		}else{
			low = mid +1;
		}
	}
	return res;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值