算法分析的一个简单的例子(运行时间计算)

    今天,我又拿起了这本之前我并不在意的书《数据结构和算法分析》。在acm的竞赛教程中徘徊许久后,我最终还是决定先打好基础,看一些基本的知识也好为将来做铺垫。其实在真正比赛时或者是在自己平时设计程序时,首先要考虑的是(毫无疑问的)这个算法的时间复杂度。倘若一个代码再简洁,再通俗易懂,如果在使用时,其效率低得让使用者感觉到了不快,这个代码是失败的。因此,为了避免无用功,在实现自己代码之前可以用运行时间计算初步的估计一下自己的这个代码所需要的时间,若时间复杂度太高,可以放下此方法去寻找新的解决算法。

    首先,我们要明确面对小数据时,其实算法的效率体现并不明显,但是如果要处理很多很庞大的数据,高效的算法尤为重要了。此处以最大子序列和为例,说说算法高效的重要性以及算法的运行时间计算的小技巧。对于最大子序列和的问题,笔者目前所了解以及想到的一共有四种方法。

    方法一,穷举法。

    穷举法,既是比较好理解的方法,也是正确性极高的方法。其代码十分简单,也通俗易懂。不多说了,上代码。

#include
#define MAX_N 100005
int a[MAX_N];
int main()
{   
	int i,j,k,n;
	int thissum=0,maxsum=0;
	scanf("%d",&n);
	for(i=0;imaxsum)
	   	   maxsum=thissum;
	   }
	printf("%d",maxsum);
	return 0;
 } 

有可能你会说,现在你还无法计算这个算法的时间复杂度,没关系,我现在就来介绍。首先,对于普通的单个for循环,一次for循环的运行时间至多是该for循环内语句(包括测试)的运行时间乘以循环的次数。然后,对于嵌套的for循环,我们可以从里往外分析,对于最里面的语句,每一次循环都要执行一次它,所以嵌套for循环的时间为执行该语句的时间乘以每一个for循环的次数的大小的乘积。之后,对于几个平行的顺序语句,在计算的时候当然是直接加和(如果只是为了估算,也可以以最大的量级作为总和)。最后,选择语句else,估计它的时间,即可用条件语句的执行时间加上两个选择中时间复杂度高的那个。这些方法虽然会估算过高,但绝不会出现估算过低的情况。所以,我们用这些方法估算出上面的方法一的时间复杂度为O(N^3)。倘若N取一万,这就算法就需要相当长的时间才能计算出结果。

方法二,优化的穷举法。

方法二的大致思路与穷举法大同小异,但是优化过后,时间复杂度有一些下降,效率得到了提高。翠花,上代码。

#include
#define MAX_N 100005
int a[MAX_N];
int main()
{   
	int i,j,k,n;
	int thissum=0,maxsum=0;
	scanf("%d",&n);
	for(i=0;imaxsum)
	   	   maxsum=thissum;
	   }
	   }
	printf("%d",maxsum);
	return 0;
 } 
可以看出,此处少了一个for循环,其时间复杂度由O(N^3)变成了O(N^2),方法二勉强能够解出N取一万时的解。但是它对十万却无能为力。可见,我们仍需改进代码。再来看方法三。

方法三,分治算法。

所谓分治算法就是把这个问题分成多个小问题,大致为两个大的子问题,然后用递归的方法处理它们,“分”是分开来求解,“治”是在两个分开解中求最优的。就拿这个问题来说,可以依据最大子序列和的这个子序列的位置来分,可以在右半部分,也可以在左半部分,还可以在中间部分。对于右半部分和左半部分,我们都可以重新地将其看成一个新序列来求解,即递归。对于中间部分,若序列为偶数,则可以从中间往两边开始寻找最大值,若为奇数则可以把最中间的那个数分给左半部分(也可以给右半部分),然后从左半与右半的分界线向两边开始寻找最大值,两者相加定为中间部分的最大值。好了,是时候上代码了。

#include
#define MAX_N 100005
int a[MAX_N];
int max3(int a,int b,int c)
{
	int max1=a;
	max1=(max10 )
	      return a[left];
	    else
	      return 0;
	center=(left+right)/2;
	maxleft=maxsubsum(a,left,center);
	maxright=maxsubsum(a,center+1,right);
	sumcenter1=0;
	maxcenter1=0;
	for(i=center;i>=left;i--)
	{
		sumcenter1 += a[i];
		if(sumcenter1>maxcenter1)
		   maxcenter1=sumcenter1;
	}
	sumcenter2=0;
	maxcenter2=0;
	for(i=center+1;i<=right;i++)
	{
		sumcenter2 += a[i];
		if(sumcenter2>maxcenter2)
		   maxcenter2=sumcenter2;
	}
	maxcenter=maxcenter1+maxcenter2;
	return max3(maxleft,maxright,maxcenter);
}
int main()
{   
	int i,Max,n;
	scanf("%d",&n);
	for(i=0;i
可以看出这个方法的每一次递归都使得运算区间对半减少,算法效率相对于前两种十分理想,在数据哪怕是十万,也能够从容的处理(至少能在接受的范围内求解出)。其时间复杂度为O(N*logN)。在这里有必要介绍介绍一下运行时间里的对数问题。如果一个算法用常数时间(O(1))将问题的大小削减为其一部分(通常是二分之一),那么该算法就是O(N*logN)。另一方面,如果使用常数时间只是把问题减少一个常数,那么这种算法就是O(N)的。

最后一个方法,一遍过算法。

这个方法我最开始是在老师那里听来的(蒋老师的c语言课),在网上也看到了一些解法,鲜有人能够道清其道理,并且看透其本质。好了,还是先给代码吧。

#include
#define MAX_N 100005
int a[MAX_N];
int main()
{
	int i,n,temp=0,max=0;
	scanf("%d",&n);
	for(i=0;imax)
		   max=temp;
		if(temp<=0)
		   temp=0;
	}
	printf("%d\n",max);
	return 0;
}
这个算法明显的优点就是只对数据遍历一次,而且在任意时刻对已读入的序列都用max记录下了最优解,这种算法近乎完美。(具体可见联机算法)

读完本篇文章后,相信你对分析程序的时间复杂度已经有了一定的了解。勤加练习,这个小技巧一定会对你的程序生涯(尤其是注重时间的时候)有极大的帮助。

你可能感兴趣的:(数据结构和算法分析c语言实现,算法,acm,技巧,c语言,初学者)