今天我们来说说算法,为什么数据结构要涉及到算法呢?而为什么在介绍具体的数据结构之前,要先讲算法呢?
正如开篇讲到的,算法是对数据如何处理的方法,不了解施加于数据上的算法需求就无法决定数据结构;反之,算法的结构设计和选择在很大程度上又依赖于作为其基础的数据结构,即数据结构为算法提供了原料。
上面只是一个形象的比喻,我们还是要给算法一个较官方的定义:算法就是用来解决某个特定课题的一些指令的集合;或者说,是由人们组织起来的准备加以实施的一系列有限的基本步骤。简单的说,算法就是解决问题的办法。
算法具有五个基本特性:
输入:算法具有零个或多个输入
输出:算法至少有一个或多个输出,没有输出的算法毫无意义
有穷性:算法必须在有限的步骤内结束。
确定性:算法的每一步骤都具有确定的含义,不会出现二义性。
可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成。
我们在数据结构中,主要涉及到对数据结构的基本操作的算法,所以在此不深入展开分析算法。之后我会单独开一个关于算法的系列文章。所以我们这里就先说说算法分析方法,以确定对数据结构操作的算法的好坏。
算法分析的目的在于改进算法,那么如何对一个算法进行评价呢?通常我们以时间复杂度和空间复杂度来衡量
时间复杂度的计算:
通常的做法是把算法中的语句执行的次数作为算法时间的量度。这种分析方法称为频度统计法。
例如:
fibonacci(int n)
{
int fn1,fn2,fn;
fn2 = 0;//------------------1
fn1 = 1;//------------------1
printf("%d %d",fn2,fn1);//------1
for(i = 2;i<=n;i++){ //------n
fn=fn2+fn1;//----------n-1
printf("%d",fn);//------n-1
fn2 = fn1;//------------n-1
fn1=fn; //------------n-1
}
}
算法中各语句的最右端分别列出了各语句的频度。整个算法的频度用f(n)表示,有
f(n)= 1 + 1 + 1 + n + 4(n-1) = 5n-1
但要全面的分析一个算法的效率,需要考虑算法在最坏的情况下的时间代价,在最好的情况下的时间代价以及在平均情况下的时间代价。
对于最坏情况,主要采用大O表示方法来描述,一般的提法是:当且仅当存在正整数c和n0,使得f(n)<=cg(n)对所有的n>=n0成立,则称该算法的渐进时间复杂度为f(n)=O(g(n)),即当实例特性n充分大时,算法的时间复杂度随n变化。在最坏的情况下,若存在一个增长上界,即ng(n),则该算法的时间复杂度增长的数量级为g(n),即算法的渐进时间复杂度O(g(n)),对于计算上面算法的例子,其时间复杂度为O(n),因为此时有g(n)=O(n)
诸如查找与排序这样的操作,通常是通过计算操作过程中所进行的元素之间比较次数的最坏情况来得到算法的时间复杂度。
常见的时间复杂度有O(1),O(n),O(n2),O(n3),O(log2n),O(nlog2n),O(2n)和O(n!)
复杂度排序为 O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!)< O(nn)
一般只讨论O(1)常数阶,O(logn)对数阶,O(n)线性阶,O(nlogn) ,O(n2)平方阶,其它的算法时间复杂度太大,不讨论它
算法的空间复杂度通过计算算法所需的存储空间实现,比较容易计算,这里也有一个公式:S(n)=O(f(n)),其中n为问题的规模,f(n)为语句关于n所占存储空间的函数。
算法还有简洁性,可读性,健壮性等要求。这里不详细讨论。
简单介绍了一下算法,下篇我们正式分析数据结构和施加到数据结构上的基本算法。希望后面写的更有条理,更有意思,更细致,能够读后留下深刻的印象。我们下篇见。