文档详情

算法进化历程之“最大连续子序列之和”.doc

发布:2017-04-19约7.92千字共8页下载文档
文本预览下载声明
算法进化历程之“最大连续子序列之和” 巧若拙(欢迎转载,但请注明出处: HYPERLINK /qiaoruozhuo \t _blank /qiaoruozhuo) 题目描述: 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 = i = j = K。最大连续子序列是所有连续子序列中元素和最大的一个, 例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。 输入: 测试输入包含若干测试用例,每个测试用例占2行,第1行给出正整数K( 10000 ),第2行给出K个整数,中间用空格分隔。当K为0时,输入结束,该用例不被处理。 输出: 对每个测试用例,在1行里输出最大和。若所有K个元素都是负数,则定义其最大和为0。 输入示例: 6 -2 11 -4 13 -5 -2 10 -10 1 2 3 4 -5 -23 3 7 -21 6 5 -8 3 2 5 0 1 10 3 -1 -5 -2 3 -1 0 -2 0 输出示例: 20 10 10 10 0 0 算法分析: 算法1: 最直接的想法是蛮力穷举。计算每一段可能的连续子序列A[i..j]之和,然后保留最大值。由于有三层循环,故时间复杂度为O(N^3)。代码如下: int MaxSubsequenceSum_1(const int A[], int n)//低效算法1 { int sum, maxSum, i, j, k; maxSum = 0; for (i=0; in; i++) { for (j=i; jn; j++) { sum = 0; for (k=i; k=j; k++)//计算连续子序列A[i..j]之和 { sum += A[k]; } if (sum maxSum) maxSum = sum; } } return maxSum; } 算法2: 仔细观察算法1,我们发现其实最内层循环是不必要的,因为我们可以直接在最外层循环中设置sum = 0;然后累计每一个A[j]值就可以得到A[i..j]之和了。由于只有两层循环,故时间复杂度为O(N^2)。代码如下: int MaxSubsequenceSum_2(const int A[], int n)//低效算法2 { int sum, maxSum, i, j, k; maxSum = 0; for (i=0; in; i++) { sum = 0; for (j=i; jn; j++) { sum += A[j]; if (sum maxSum) maxSum = sum; } } return maxSum; } 算法3: 虽然算法2把时间复杂度减小到O(N^2),但还算不上高效的算法,我们可以采用一种“分治”策略,把序列分成左右两个部分,则最大子序列和可能在三处出现:左半部,右半部,或者跨越数据的中部而占据左右两半部分。前面两种情况可以递归求解,第三种情况需要计算出左半部最大和(必须包含左半部最右元素)以及右半部最大和(必须包含右半部最左元素),然后将这两个和加在一起。最后返回三者的最大值。 由于采用了分治算法,所以时间复杂度可以减小到O(NlogN)。 代码如下: int MaxSubsequenceSum_3(const int A[], int n)//分治算法 { return MaxSubSum(A, 0, n-1); } int MaxSubSum(const int A[], int left, int right)//分治算法子程序 { int maxLeftSum, maxRightSum; int maxLeftBorderSum, maxRightBorderSum; int leftBorderSum, rightBorderSum; int mid, i; if (left == right) return (A[left] 0) ? A[left] : 0; mid = (left + right) / 2; maxLeftSum = MaxSubSum(A, left, mid); //递归计算左半部子序列最大和 maxRightSum = MaxSubSum(A, mid+1, right);//递归计算右半部子序列最大和 maxLeftBorderSum = leftBorderSum = 0; for (i=mid;
显示全部
相似文档