数据结构之算法初涉(2)
1 前言
上节我们介绍了数据结构的基本概念,这次我们来介绍一下算法,数据结构和算法也是类似的关系。好的算法能大大提高一个程序的运行效率,例如:高斯算法求1,2,3...n个连续数的和,高斯给了我们n*(n+1)/2的公式,比起一个个加起来效率高的多,可见好的算法不仅对于现实世界,对于程序世界也是极好的~
2 详述
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或者多个操作。
2.1 算法的特性
·输入输出:具有零个或者多个输入,一个或者多个输出;
·有穷性:算法在执行有限的步骤后,自动结束而不会出现无限循环,并且每一个步骤在可接受的时间内完成;
·确定性:算法的每一个步骤都具有确定的含义,不会出现二义性;
·可行性:算法的每一步都是可行的,每一步都能执行有限次数完成。
2.2 算法设计的要求
·正确性:算法应该至少具有输入,输出和加工处理无歧义性,能正确的反应问题的需求,能够得到问题的正确答案;
·可读性:便于阅读,理解和交流;
·健壮性:当输入不合法时候,算法也能做出相关的处理,而不是产生异常或者莫名其妙的结果;
·时间效率高和存储量低:算法设计应该尽量满足时间效率高和存储量低的需求。
2.3 算法效率的度量方法
2.3.1 事后统计方法
这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同的算法编制的程序进行时间比较,从而确定算法效率的高低。
缺陷:
·必须实现编制好程序,需要花费大量时间和精力;
·时间的比较依赖计算机硬件和软件等环境因素;
·算法测试数据设计困难,有时候需要大规模的测试数据。
所以我们一般不考虑这种统计方法
2.3.2 事前分析估算方法
在计算机程序编制前,依据统计方法对算法进行估算。
一个程序的运行时间,依赖于算法的好坏和问题的输入规模。所谓的问题输入规模是指输入量的多少。
在分析程序的运行时间时,最重要的是把程序看成是独立于程序设计语言的算法或者一系列的步骤。
2.4 函数的渐近增长
给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐近快要g(n)。
对于最高次项的指数大的,函数增长随着n的增长,结果也会变得增长特别快。
从上图看出来:判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应关注主项(最高阶项)的阶数。
2.5 时间复杂度
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间度量,记作:T(n) = O(f(n))。它表示随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。
这样用大写O()来体现算法时间复杂度的记法,成为大O记法。
一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。
2.6 推导大O阶方法
2.6.1 常数阶
这个算法函数是f(n)=3,根据推导公式把3改为1,则为:O(1),成为常数阶。
2.6.2 线性阶
分析算法的复杂度,关键就是分析循环结构的运行情况。
int i;
for(i = 0;i < n;i++){
/*时间复杂度为O(1)的程序步骤序列*/
}
上面的代码时间复杂度为O(n)。因为循环体中的代码要执行n次。
2.6.3 对数阶
由于每次count乘以2之后,就距离n更近了一分。也就是说有多少个2相乘后大于n,则会退出循环。由2的x次方等于n,得到。所以这个循环的时间复杂度为O(logn)。
2.6.4 平方阶
外层循环复杂度为O(n),内部又循环了n次。所以这段代码的时间复杂度为。
2.7 常见的时间复杂度
时间复杂度的大小顺序依次为:
2.8 最坏情况与平局情况
最坏情况运行时间是一种保证,那就是运行时间不能再坏了。在应用中,这是一种最重要的需求,通常,除非特别指定,我们提到的运行时间都是最坏情况的运行时间。
平均运行时间是所有情况中最优意义的,因为它是期望的平均时间。
2.9 算法空间复杂度
算法空间复杂度通过计算算法所需要的存储空间实现,算法空间复杂度计算公式记作:S(n) = O(f(n)),其中,n为问题规模,f(n)为语句关于n所占存储空间的函数。
若算法执行所需的辅助空间对于输入和输出量而言是一个常数,则称此算法为原地工作。空间复杂度为O(1)。
通常我们所说的复杂度指的都是时间复杂度。