上章学习了数据结构,了解了一些相关概念以及数据结构的定义,我们了解到数据结构通常与算法是分不开的,没有算法的数据结构犹如演双簧少了一个人,失去了他的乐趣,本章开始学习算法。(没错,要有难度了,做好准备!)
首先来看算法的定义:
算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
我们为什么学习算法呢?假设我们让计算机计算1+2+3+4+…+99+100的值,大多数人首先想到的程序是用1+2=3,3+3=6,6+4=10…这种方法循环100次,这些循环,看似对于计算机来说微不足道,但如果要加到上亿呢?但应用等差数列的求和算法后,我们却可以更高效率的得出计算结果,就算加到上亿,也不过是瞬间的事,算法可以极大的提升程序运行的效率,更快,更高效的实现我们的目标。计算机是死的,但人是活的,对于给定的问题,我们要灵活选择合适的算法,以更高效的解决问题。
在算法定义中,提到了指令,指令能被人或计算机等计算装置代替,它可以是计算机指令,也可以是我们平时的语言文字。每条指令表示一组操作,每一个操作都完成特定的功能,这就是算法。
算法具有五个基本特性:输入、输出、有穷性、确定性和可行性。
输入输出:算法具有零个或多个输入,但至少有一个或多个输出,输出的形式可以是打印输出,也可以返回一个或多个值。
有穷性:,顾名思义,指算法在执行有限的步骤之后,自动结束,不会出现无限循环,并且每一个步骤在可接受的时间内完成。例如死循环代码以及那种运行二十年才结束的代码(假如真的有的话),都不满足有穷性。
确定性:算法的每一步骤都具有确定的含义,不会出现二义性,不能出现模棱两可的结果,输出必须是确定的。
可行性:算法的每一步都必须是可行的,也就是说,每一步都能够通过执行有限次数完成,可行性意味着算法可以转换为程序上机运行,并得到正确的结果。
算法不是唯一的,没有最好的,只有最适合的,但尽管算法不唯一,优秀的算法还是有些共同的特征,这就是算法设计的要求。
1、正确性:正确性是算法设计要求的前提,算法的正确性是指算法至少应具有输入、输出和加工处理无歧义性、能正确反映问题的需求、能够得到问题的正确答案。
算法的“正确”分为四个层次:
设计算法要提高效率,所谓效率,大都指的是算法程序执行时间,那如何度量一个算法的执行时间呢?
事后统计法(×):这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。
但事后统计法存在很大缺陷,其一就是可能会在设计好程序后发现这是很糟糕的算法,从而浪费大量的精力和时间,因此不予采纳。
事前分析估算法(√):由于设计程序是最耗费时间的步骤,因此考虑最好在设计程序之前选出合适的算法,由此,产生了一种叫做事前分析估算的方法。
定义:在计算机程序编制前,依据统计方法对算法进行估算。
高级程序语言编写的程序所消耗的时间取决于下列因素:
**定义:①给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐进快于g(n)。**所谓的“增长渐进”,我们可以理解为不同函数的斜率。函数的渐进增长,可以理解为函数的单调递增。
②在利用函数判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数。简而言之,在输入规模大到一定程度时,算法的效率取决于该函数的最高次数。
由①②可得推论:某个算法,随着n的增大,它会越来越优于另一算法,或者越来越差于另一算法。这就是事前估算方法的理论依据,通过算法时间复杂度来估算算法时间效率。
定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。人话就是,把T(n)记作算法的时间复杂度,也可以说是算法的时间量度,T(n)的增长率和f(n)的增长率相同,T(n)和f(n)成正比,T(n)=O(f(n))。
因此可知,一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。(即f(n)渐进增长小,基本操作次数少,效率高)。
那么如何分析一个算法的时间复杂度呢?及如何推导大O阶呢?以下为推导方法(可借鉴结论②):
1、得出基本操作执行次数与输入规模的函数。
2、用常数1取代所有加法常数。
3、在修改后的运行次数函数中,只保留最高阶项。
4、如果最高阶项存在且不是1,则则去除与这个项相乘的常数。
得到的结果就是大O阶。
例:常数阶:顺序结构(执行的次数与n的大小无关,执行时间恒定的算法),具有O(1)的时间复杂度。单纯的分支结构,其时间复杂度也是O(1)。
线性阶、对数阶、平方阶、nlogn阶、立方阶、指数阶
常用的时间复杂度从小到大的排序是:
从立方阶往后的时间复杂度都过大,因此一般不去讨论。
总之,计算时间复杂度关键是找出基本操作次数,再通过上面介绍的方法得出结果。
最坏情况运行时间是一种保证,在应用中,这是一种最重要的需求,通常,我们提到的运行时间都是指最坏情况的运行时间。
平均运行时间是最有意义的,因为它是期望的运行时间。
对算法的分析,一种方法是计算所有情况的平均值,,这种时间复杂度的计算方法称为平均时间复杂度。另一种方法是计算最坏情况下的时间复杂度,这种方法称为最坏时间复杂度。一般情况下,都是指最坏时间复杂度。
算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。
算法的空间复杂度解释较少,通常来说,只要算法不涉及到动态分配的空间以及递归、栈所需的空间,空间复杂度通常为O(1)。
第二章的学习终于结束了啊,总体来说还是一些关于定义概念的东西,通过本章的学习,可以对自己的算法水平有一个更清晰的认知,通过时间复杂度的估算确定自己算法的效率,从而做出更高效率的算法。学习好难,,继续加油!