数据结构mooc课小结(1)

来源:互联网 发布:手机机顶盒遥控器软件 编辑:程序博客网 时间:2024/06/06 00:14
第一课, 讲了最大子序列和的算法。 从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
原创粉丝点击