数据结构mooc课小结(1)

第一课, 讲了最大子序列和的算法。 从O(n^3) 逐步讲解到O(n) 的算法。

O(n^3) 和 O(n^2) 的算法非常直白,无需赘述。

O(n log n)的分治法,值得去实现, 锻炼一下代码能力。
主要思路是: 二分的时候,目标序列只可能有三种情况: 左边,右边,横跨分界线。
选三者中最大的就好了。
我觉得最核心的就是怎么处理 横跨分界线的情况,这个是我一开始没想出来的。
后来看了别人的做法,就是从中间元素开始向左边求和,求出mid到left中,最大的序列和, 然乎再从mid到right找连续的最大序列和。 两者相加即可。

以  4,-3,  5,-2,  -1,2,  6,-2 为例,
分治到 4 的时候,返回4, -3的话返回0,还有横跨中间的情况也得到4.
那么[4   -3]所得结果就是 4 0 4.
同理[5 -2]  则是  5 0 5
[ 4   -3   5   -2] 的结果4 5 6,  6是怎么来的?从mid到left 得到1, mid向右得到5,加起来得到6
下面是当时参考的别人的代码
int maxSubArray(int nums[], int left, int right){
    int maxLeftSum, maxRightSum;
    int maxLeftBorderSum, maxRightBorderSum;
    int leftBorderSum, rightBorderSum;

    if(left == right)
        if(nums[left] > 0)
            return nums[left];
        else
            return 0;

    int mid = (left + right) / 2, i;
    maxLeftSum = maxSubArray(nums, left, mid);
    maxRightSum = maxSubArray(nums, mid + 1, right);

    maxLeftBorderSum = 0, leftBorderSum = 0;
    for(i = mid; i >= left; i--){
        leftBorderSum += nums[i];
        if(leftBorderSum > maxLeftBorderSum)
            maxLeftBorderSum = leftBorderSum;
    }

    maxRightBorderSum = 0, rightBorderSum = 0;
    for(i = mid + 1; i <= right; i++){
        rightBorderSum += nums[i];
        if(rightBorderSum > maxRightBorderSum)
            maxRightBorderSum = rightBorderSum;
    }

    return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum);
}




O(n)的做法需要多一点分析,利用最大序列和的某些特性:
1、负数开头的序列,都不可能是目标序列,因为总可以去掉这开头的负数,使得序列和更大。
2、特性1的推广,任何和为负数的序列都不可能是 目标序列的一部分,因为总可以去掉开头这部分和为负的子序列,获得更大的序列和。

那么求和的时候,一旦部分和是负的,就可以抛弃了,从下一个数开始从零开始累计即可。
int sum = 0, max=0;
for (int i=0; i<n; i++)
{
	sum += a[i];
	if (sum <=0 ) sum = 0;  //前一段序列和不是正数,从头再来 
	if (sum > max) max = sum;
}

序列和还有一些别的变种,例如要给出最大序列的开头和结尾。
这里涉及一个细节, 序列和为0的子序列可以作为目标序列的开头, 也可以忽略掉。 这个地方只能根据需要自行调整了。
下面的例子是,尽可能取目标序列中开头 和结尾下标最小的那个。
也就是说,开头和为0的都要选,结尾和为0的都不要
for( i=0; i<n; i++)
{
	scanf("%d", a+i);
	sum += a[i];
	e = i;
	
	if( sum  < 0)
	{
		sum = 0;
		s = i+1;
		e = s;
	}
	
	if( sum > max )
	{
		max = sum;
		start = s;
		end   = e;
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值