LeetCode:数组的最大子序和

LeetCode链接:最大子序和

最简单暴力的就是两层遍历,求出每个子序列的和,然后取其最大值。

文中给出了另外一种算法,被提炼称呼为“贪心算法”,但题解并未给出另外一个更有用的数据:子序和有了,起止索引在哪

与其上来就七上八下用一套武功招式将这题破解,我更倾向于从基本思想着手,自然地创立出招式,从而将题目破解。我们接下来将用简单的逻辑,用实际情况对逻辑进行升级和修正,最后得出真正可用的方法。

假如数组为-1 2 -1 2 -3,我们肉眼一眼就看出最大子序和3,序列为2 -1 2。OK,现在从逻辑角度来计算最大子序和。

从正常思维来看,首先第一个-1就要被排除,为什么?我作为一个整数,去和一个-1即负数相加,肯定会使得我变得更小,我还加它干什么?-1跳过!

接下来是2,加2会使得整数更大,保留。

接下来是-1,问题来了:第一个-1被跳过了,这个-1怎么处理?显然这个-1不能直接跳过,为什么?因为序列是连续的,-1如果被废弃,那前面的2不也被丢掉了吗?!如果不废弃,应该怎么处理它?因为前面的2被保留了,所以要想使用-1,必须连带2一起用,2 + -1 = 1 > 0,所以2和-1组成的组合值得被保留。

wait!事情发展到这里,好像和开始的思想发生点了变化?我们开始认为负数应当舍弃,正数应当保留,而现在要把2 -1作为一个整体,这意味着什么?意味着我们处理的目标从单个数值变成了一组数值!我们这里称为小数组。

小数组的和>0,则我们认为将它纳入子序列是有价值的。

小数组的和<0,则它会拖累我们,使得子序和变小,丢弃!

将思路进一步精炼:每次处理一个数据的时候,我们都判断之前小数组的和是否为负数,如果它是负数,将它丢掉!从这个数据开始重新扫描;如果它是正数,则相加,然后继续向后扫描。

于是我们有以下伪代码

	int maxSubArray(vector<int>& nums)
	{
		int sum = nums[0];
		for (int i = 0; i < nums.size(); ++i)
		{
			if (sum < 0)
			{ //之前序列小于0,丢弃!重新扫描
				sum = nums[i];
			}
			else
			{//之前序列大于等于0,可以保留
				sum += nums[i];
			}
		}
	}

一个重要的问题:上面代码中并没有保存最大值,OK,我们添加一个max作为最大值。理想情况下,如果sum一直连续,则max将被更新为最新的sum,但如果sum被打断重新开始了,max是不是也要重新开始呢?我们持怀疑态度,如数组2 -1 -3 1,前三个数据和为-2,于是从第4个1重新开始,但1好像比第一个数2还小?看来这种情况max不一定要更新嘛。Good!对于这种情况的处理,我们可以归结为将max设置为sum和max的最大值,如果新的sum比max还小,我们就保持max不变!又因为我们已经将第一个数作为默认的和了,所以循环从1开始,于是有如下代码

    int maxSubArray(vector<int>& nums) 
    {
		int sum = nums[0];
		int max = sum;
		for (int i = 1; i < nums.size(); ++i)
		{
			if (sum < 0)
			{ //之前序列小于0,丢弃!重新扫描
				sum = nums[i];
			}
			else
			{//之前序列大于等于0,可以保留
				sum += nums[i];
			}
			//无论是丢弃情况,还是保留情况,都需要和max作对比
			//丢弃情况:对于2 -1 -3 10来说,到10时,前面和为-2全部丢弃,需要对比10和2,取10为最大值
			//保留情况,对于2 -1 -3来说,到-1时,和为1,需要和2对比,取2为最大值
			if (sum > max)
				max = sum;
		}
		return max;
    }

至此,我们已经循着开始不完善的思想走到了现在,可以说已经解决了这个问题。我们查看一下LeetCode的解决代码

public int maxSubArray(int[] num) {
    int length = num.length;
    int cur = num[0];
    int max = cur;
    for (int i = 1; i < length; i++) {
        cur = Math.max(cur, 0) + num[i];
        //记录最大值
        max = Math.max(max, cur);
    }
    return max;
}

形式有差异,但做的事情是一模一样的。

PS:本文没有处理最大子序列的起止索引,有兴趣的同学可以交流一下^_^

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值