两种算法的比较
对于学过编程的人面对求1+2+3+4+.................+100的结构的求和,很多人想到程序实现的第一直觉应该是:
int i,sum = 0, n = 100;
for(i = 1; i < n ; i++)
{
sum = sum + i;
}
printf(" %d ",sum);
这种方法是最简单的程序设计之一。
然而我们在面对这种有计算技巧的算法来说。我们更多的应该考虑如何结合我们平时在数学上学习的一些计算公式来简化计算的复杂度:对于简单的求和公式我们应该在高中就学过求和公式的:(1 + n )*n/2 这就是高斯求和公式。
int i , sum = 0, n = 100;
sum = ( 1 + n)*n/2;
printf(" %d ",sum);
这样的求和公式才是我们真正需要的,也是我们实现对我们的数学基础知识学以致用的体现。
我们对我们平时在课本里学到的一些堆问题的描述和解决的办法都称之为算法:
算法------是解决特定问题求解步奏的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
函数的渐进增长:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n > N,f(n)总是比g(n)大,那么,我们说f(n) 的增长渐进快于g(n)。
算法时间复杂度的定义:
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级,算法的时间复杂度,也就是算法的时间度量,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。
推导大O阶方法:
1. 用常熟1取代运行时间中的所有加法常数。
2. 在修改后的运行次数函数中,只是留最高阶项。
3. 如果最高阶项存在且不是1,则去除与这个项相乘的常数。
得到的结果就是大O阶。
常数阶:
首先顺序结构的时间复杂度。下面这个算法,也就是刚才的高斯求和算法
int i , sum = 0, n = 100;
sum = ( 1 + n)*n/2;
printf(" %d ",sum);
其运行次数函数是f(n) = 3,根据我们推导大O阶的方法,第一步就是把常数3改为1.在保留最高阶项时发现,它根本没有最高阶项,所以这个算法的时间复杂度是O(1)。另外,我们事项以下,如果这个算法当中的语句sum = (1 + n)*n/2有10句,即:
int i , sum = 0, n = 100;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
sum = ( 1 + n)*n/2;
printf(" %d ",sum);
事实上无论n为多少,上面的两段代码就是3次和12次执行的差异。这种与问题的大小无关,执行时间恒定的算法,我们称之为具有O(1)的时间复杂度,又叫常数阶。
对于分支结构而言,无论是真,还是假,执行的次数都是恒定的,不会随着n的变化而发生变化,所以单纯的分支结构,其时间复杂度也是O(1)。
线性阶:
线性阶的循环结构会复杂很多,要确定某个算法的阶次,我们常常需要确定某个特定语句或某个语句集运行的次数,所以要分析算法的复杂度,关键就是要分析循环结构的运行情况。例如下面的语句的时间复杂度是O(n)。
int i;
for(i = 0; i < n; i++);
对数阶:
int count = 1;
while(count < n)
{
count = count * 2;
}
由于每次count乘以2之后,就距离n更近一分,也就是说,有多少个2相乘后大于n,则会退出循环。由于2^x=n 得到x= log2n.所以这个循环的时间复杂度为O(log n)。
平方阶:
int i,j;
for(i = 0; i < n; i++)
{
for(j = 0; j< n; j++)
{
<span style="white-space:pre"> </span>//时间复杂度为O(1)的程序步骤
}
}
对于外层的循环,不过是内部这个时间复杂度为O(n)的语句,再循环n次。所以这段代码的时间复杂度为O(n^2)。如果外部循环的次数为m,那么时间复杂度就是O(n x m)。
对于下面这种情况:
int i,j;
for( i = 0; i < n; i++)
{
for( j = i; j < n; j++)
{}
}
由于当i = 0时,内循环执行了n 次,当i = 1时,执行了n - 1次,……当i= n - i时执行了1次,所以总的执行次数为:
n + (n - 1) + ( n -2) + ........+ 1 = n(n + 1)/2 = n^2 /2 + n/2
我们推导大O阶的方法,得出这段代码的时间复杂度为O(n^2);
执行次数函数 | 阶 | 非正式术语 |
12 | O(1) | 常数阶 |
2n + 3 | O(n) | 线性阶 |
3n2 + 2n + 1 | O(n2) | 平方阶 |
5log2n + 20 | O(logn) | 对数阶 |
2n + 3nlog2n + 19 | O(nlogn) | Nlogn阶 |
6n3 + 2n2 + 3n + 4 | O(n3) | 立方阶 |
2n | O(2n) | 指数阶 |
常用的时间复杂度所耗费的时间从小到大依次是:
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n^n)