11、最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

这道题有点难度
我的这个代码超时了

int max = Integer.MIN_VALUE;
		int min = 0;
		
        for(int i = 0; i < nums.length; i++){
        	for(int j = i; j < nums.length; j++){
        		min = 0;
        		for(int k = i; k < j+1; k++){
        			min += nums[k];
  
        		}
        		if(min > max)
        			max = min;
        		
        	}
        }
		return max;

将上面的代码进行优化,只用两层循环,改成这样的,就是说外层是对所有元素起点的循环,内层是对所有元素的总和进行求和计算,在进行计算求和的过程中,不断和max进行比较,如果发现其值大于max则更新max的值
代码如下,但是也是刚刚通过了,打败了10%…

int max = Integer.MIN_VALUE;
		int min = 0;
		for(int i = 0; i < nums.length; i++){
			min = 0;
			for(int j = i; j < nums.length; j++){
				min +=nums[j];
				if(max < min){
					max = min;
				}
			}			
		}
		return max; 

尝试使用递归来进行解决
首先就是最大要么出现在
1、左半部分
2、右半部分
3、包含了中间的元素

public static int maxSubsequenceSum(int[] a, int left, int right) {
    if(left == right) { //Base case
        if(a[left] > 0) {
            return a[left];
        } else {
            return 0; //保证最小值为0
        }
    }
    
    int center = (left+right)/2;
    int maxLeftSum = maxSubsequenceSum(a, left, center); //递归调用,求左部分的最大和
    int maxRightSum = maxSubsequenceSum(a, center+1, right);//递归调用,求右部分的最大和
    
    int leftBorderSum = 0, maxLeftBorderSum = 0;//定义左边界子序列的和
    for(int i=center; i>=left; i--) {//求左边界的最大和(从右边开始往左求和)
        leftBorderSum += a[i];
        if(leftBorderSum > maxLeftBorderSum) {
            maxLeftBorderSum = leftBorderSum;
        }
    }
    
    int rightBorderSum = 0, maxRightBorderSum = 0;//定义右边界子序列的和
    for(int i=center+1; i<=right; i++) {//求右边界的最大和(从左边开始往右求和)
        rightBorderSum += a[i];
        if(rightBorderSum > maxRightBorderSum) {
            maxRightBorderSum = rightBorderSum;
        }
    }
    
    //选出这三者中的最大值并返回(max3(int a, int b, int c)的实现没有给出)
    return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}

转https://my.oschina.net/itblog/blog/267860
看一下排名靠前的代码,看看别人是怎么想的

首先定义一个长度为nums数组长度的数组subSums
遍历序列的时候对Sum进行累计,如果Sum累积后小于0的话就把Sum重置为下一个数字,每次更新Sum的最大值。最后便能求出最大值。其实这个算法就是把序列分为好几块,每一块满足:对于任意k,前k个数的和不会小于0(小于0就会分裂成两块了),当前i个数的和大于最大值时就进行更新,而最大值的左边界就是该块序列的第一个,右边界是第i个。时间复杂度为O(n),而且可以一边读取一边处理,不需要开辟数组来存,空间也很省

摘自https://blog.csdn.net/samjustin1/article/details/52043369
还需要好好琢磨一下

             int subSums = nums[0];
	       
	        int max = subSums;
	        for (int i=1; i<nums.length; i++) {
	            subSums = subSums > 0 ? subSums + nums[i] : nums[i];
	            if (subSums > max) {
	                max = subSums;
	            }
	        }
	       
	        return max;
        
        return max;
    }
}

动态规划解法
dp做法是很普遍的做法,只要想出状态转移方程就可以很快做出来了。
状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}. (sum[i]记录以a[i]为子序列末端的最大连续和。)在dp的过程中便可以更新sum数组的最大值以及两个边界。 其实完全可以不用开数组,累计sum直到sum + a < a,把sum赋值为a,更新最大值就行了。你会发现这跟第4种方法是一样的。。。只是判断条件不一样,一个是sum <= 0一个是sum + a < a。。。(其实是一样的。。。)
https://blog.csdn.net/samjustin1/article/details/52043369
代码跟第四种基本一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值