数据结构一:最大子序列和问题

来源:互联网 发布:php class 内调用方法 编辑:程序博客网 时间:2024/04/30 12:11

在MOOC学习了C语言之后,深深觉得之前对C语言的理解以及使用都只是在隔靴搔痒,其中除了当时自己没有那么认真以外,老师的教学水平差距应该也是一个必不可少的差距。显然浙大老师的讲课方式以及经验和专业性都让我觉得获益匪浅。所以在学习了C语言基础之后继而学习了进阶版C语言,无疑这比基础要难得多。同时我还备选了浙大另外两位老师的数据结构,才听了第一次课,也是浑身的满足感以及成就感。

废话不多说,现在讨论的是最大子序列和问题。用C语言写数据结构及算法真的是比用JAVA有天然的优势,光输入输出就省了很多事好嘛!!

问题描述:给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

输入格式:

输入第1行给出正整数 K (<= 100000);第2行给出K个整数,其间以空格分隔。

输出格式:

在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。

输入样例:
6-2 11 -4 13 -5 -2
输出样例:
20
这里面的算法其实老师在课上就已经讲过了,她提到了三种算法。第一种算法直接先记录序列头i,序列尾j,以及从i到j的序列之和,一次一次的叠加算出最后最大的和

第一种:int MaxSubseqSum1( int A[], int N )
{ int ThisSum, MaxSum = 0;
int i, j, k;
for( i = 0; i < N; i++ ) { /* i置 是子列左端位置 */
for( j = i; j < N; j++ ) { /* j置 是子列右端位置 */
ThisSum = 0; /* ThisSum 是从A[i] 到A[j]和 的子列和 */
for( k = i; k <= j; k++ )
ThisSum += A[k];
if( ThisSum > MaxSum ) /* 大 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 果 则更新结果 */
} /* j束 循环结束 */
} /* i束 循环结束 */
return MaxSum;
}  T( N ) = O( N 3  )

第二种,我们发现每次在i确定的时候,j只是每次把j+1的值加到原序列里根本无需k来记录i到j之间的序列每次相加

int MaxSubseqSum2( int A[], int N )
{ int ThisSum, MaxSum = 0;
int i, j;
for( i = 0; i < N; i++ ) { /* i置 是子列左端位置 */
ThisSum = 0; /* ThisSum 是从A[i] 到A[j]和 的子列和 */
for( j = i; j < N; j++ ) { /* j置 是子列右端位置 */
ThisSum += A[j];
/* 对于相同的i ,不同的j ,只要在j-1 次循环的基础上累加1 项即可*/
if( ThisSum > MaxSum ) /* 大 如果刚得到的这个子列和更大 */
MaxSum = ThisSum; /* 果 则更新结果 */
} /* j束 循环结束 */
} /* i束 循环结束 */
return MaxSum;
}

T( N ) = O( N 2  )

第三种,利用分而治之的办法,此处没有代码,只有具体思想。

第四种:在线处理,“ 在线” 的意思是指每输入一个数据就进行 即时处理,在任
何一个地方中止输入,算法都能正确给出当前的解。

int MaxSubseqSum4( int A[], int N )
{ int ThisSum, MaxSum;
int i;
ThisSum = MaxSum = 0;
for( i = 0; i < N; i++ ) {
ThisSum += A[i]; /* 加 向右累加 */
if( ThisSum > MaxSum )
MaxSum = ThisSum; /* 果 发现更大和则更新当前结果 */
else if( ThisSum < 0 ) /* 负 如果当前子列和为负 */
ThisSum = 0; /* 之 则不可能使后面的部分和增大,抛弃之 */
}
return MaxSum;
}  T( N ) = O( N)

后来在刷PAT题的时候,碰到了一个稍复杂的相关题目,除了需要输出最大子序列的值以外还要把这个序列的头和尾的编号值输出,本人只想到利用每次改变MaxSum的时候记录i,后来发现i一直会变根本无法记录。无奈去讨论区看到一个同学的答案是利用记录j的值,然后通过cnt记录子序列的长度然后j-cnt就能获得i的值,类似的想法不过别人想的更全面。假使当时多想一会儿,估计就想出来了。题目输入输出如下:

输入:

10-10 1 2 3 4 -5 -23 3 7 -21
输出:
10 1 4
代码如下:
#include<stdio.h>int main(){int N=0;scanf("%d",&N);int MaxSum=0 ,ThisSum=0;int i ;int start =0, end =0, cnt = -1;if(N<= 100000 && N > 0){int a[N];int i =0;while(i < N){scanf("%d",&a[i]);i++;}for(i= 0;i<N;i++){ThisSum += a[i];cnt++;if(MaxSum < ThisSum){MaxSum = ThisSum;end = a[i];start = a[i-cnt];}else if(ThisSum < 0){ThisSum = 0;cnt = -1;}}printf("%d %d %d",MaxSum,start,end);}else{printf("0");}return 0;}
一步一步的努力学习,数据结构也是一个不断积累经验的过程吧~




0 0
原创粉丝点击