解决最大子数组之和的三种方法

所谓最大子数组问题,就是在给定的一串包含正数,负数的数组中,找出最大的子数组的和
例如:
输入:1,-2,3,10,-4,7,2,-5

最大子数组和为18

一般而言,有三种办法可以用于解决这个问题

1.暴力破解法

暴力破解法就是将所有的子数组的和全部加起来,取最大的

算法最简单,但是时间复杂度最高

时间复杂度:O(n)=n^3

var sum=-1000;
for (var i=0;i<arr.length;i++) {
	//遍历从i开始的所有子数组
	for (var j=i;j<arr.length;j++) {
		var thisSum=0;
		//累计求和
		for (var k=i;k<=j;k++) {
			thisSum+=arr[k];
		}
		if (thisSum>sum) {
			//记录最大子数组的位置和长度
			sum=thisSum
		}
	}
}
console.log(sum);//sum=18


2.分治法

分治法的核心思想就是将大问题分解为小问题,再将小问题逐个解决,然后从小问题的解得到原问题的解

如果把数组从任一点(一般取中点)分为两个数组,那最大子数组只能存在于三个位置
    1.左数组
    2.右数组
    3.左数组最大后缀和右数组最大前缀的拼接(我称为中间数组)
然后把分得的两个数组使用递归算法继续分割,直到每个子数组只含有一个元素
此时两两进行判断:
若左数组较大,并返回左数组的值,右数组一样

若中间数组较大(此处即左右最大前缀和后缀的和,则返回这个和)

算法时间复杂度:O(n)=nlogn

function findMaxChildArr(arr,left,right){	
    if (left==right) {
    //如果左等于右,则说明这个子数组只存在一个元素,则返回它用于比较
        return arr[left];
    }else{
        //递归思想
        var middle=parseInt((left+right)/2);
        var leftMax=findMaxChildArr(arr,left,middle);//寻找左侧数组最大值;
        var rightMax=findMaxChildArr(arr,middle+1,right);//寻找右侧数组最大值;
        var middleMax=findMiddleMax(arr,left,right,middle);//寻找中间最大值
        if (leftMax>=rightMax&&leftMax>=middleMax) {
            return leftMax;
        }else if(rightMax>=leftMax&&rightMax>middleMax){
            return rightMax;
        }else{
            return middleMax;
        }
    }
}
			
function findMiddleMax(arr,left,right,middle){
    var lmidMax=-1000;
    var rmidMax=-1000;
    var sum=0;
    //找到向左的最大前缀
    for (var i=middle;i>=left;i--) {
        sum+=arr[i];
        if (sum>lmidMax) {
            lmidMax=sum;
        }
    }
    //找到向右的最大后缀
    sum=0;
    for (var i=middle+1;i<right;i++) {
        sum+=arr[i];
        if (sum>rmidMax) {
            rmidMax=sum;
        }
    }
    return (lmidMax+rmidMax);
}
var max=findMaxChildArr(arr,0,arr.length);
console.log(max)//18

3.动态规划法

定义数组p,p[i]表示从arr[0]开始的前i项和,定义p[-1]=0,设arr=[1,-3,5,-2,4]
则有:
p[0]=max(p[-1]+arr[0],arr[0])  p[0]=max(0+1,1)=1;
p[1]=max(p[ 0]+arr[1],arr[1])  p[1]=max(1-3,-3)=-2;
p[2]=max(p[ 1]+arr[2],arr[2])  p[2]=max(-2+5,5)=5;
p[3]=max(p[ 2]+arr[3],arr[3])  p[3]=max(5-2,-2)=3;
p[4]=max(p[ 3]+arr[4],arr[4])  p[4]=max(3+4,4)=7;

所以该数组的最大子数组之和为7


不难发现p[i]的值是否改变的判断依据是p[i-1]+arr[i]是否大于arr[i]

p[i-1]+arr[i]>arr[i]?p[i]=p[i-1]+arr[i]:p[i]=arr[i];

进一步思考,能否只用一个变量来起到代替数组p的作用呢?答案是肯定的

经过观察,我们发现取到arr[i]这个值的前提是,arr[i]需要给我们的最大子数组之和的这个和带来正面作用,也就是让子数组之和更大,这样的话我们才会取到这个arr[i]所以,我们可以用一个变量thisMax表示当前累加的和,如果它加上arr[i]之后比原来的max更大(也就是对max起正面作用),那我们就把这一项算入到最大子数组当中,另外,如果thisMax+arr[i]之后导致arr[i]比原先还要小,我们就可以理解为thisMax在求最大子数组之和这件事上没有发挥正面作用,所以我们以arr[i]为第一项重新开始累计最大子数组之和.

这样,只需要一个循环我们就可以解决问题

时间复杂度:O(n)=n

(动态规划是真滴强)

var max=0;
var thisMax=0;
for (var i=0;i<arr.length;i++) {
    thisMax+=arr[i];
    //若thisMax有没有令arr[i]变小,则从arr[i]重新开始累计
    if (thisMax<arr[i]) thisMax=arr[i];
    //若当前的thisMax已经比上一个max大,则将thisMax记为max
    if (thisMax>max) max=thisMax;
}
console.log(max);


  • 7
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值