上一章只是一个简介,对于数据结构的简短介绍,所以在难度上可能并不是那么难,不过从这一章开始,难度就会有明显的提升了
2,算法
其实,算法就是我们为了解决我们在编程中遇到的问题,而打出的一连串操作指令,其中.每一个或是几个指令会去完成我们给他赋予的功能,这就是算法,算法具有五个基本特性,分别是输入,输出,有穷性,确定性,可行性
(1),输入,输出
在算法中,输入可以是零个,就像是我们刚开始接触c语言时,自己编写的最简单的程序,打印hello world,这个程序并不需要你的输入,但是算法必须有输出,没有输出的算法没有利用价值
(2),有穷性
你编写的代码,必须要有穷,也就是不能一直循环下去,并且循环要在一个可接守的时间内结束,
(3),确定性
算法的每一步骤都要有其唯一的意义,不能有歧义
(4),可行性
算法的每一步都必须可行,每一步都在可接受的有限次数完成,因为受限与编程工具和人的大脑,这条是必须的
我们对算法设计的要求,应该满足一下几点,首先,算法必须准确无误,首先,算法不能有语法错误,其次,对于我们输入的数据,算法应该有正确的输出来响应我们的输入,而在这一点中,我们输入的数据,应该从简单的符合条件的数据,到不符合条件的数据,再到某些极其刁钻的数据,都应该有一个正确的输出
(5),可读性
便于理解和阅读,可以让人们相互交流
(6),健壮性
当输入的数据不合法时,算法也能根据事先编好的代码,来做出指示,而不是输出一堆没用的数据,
(7),时间效率高,存储量低
顾名思义,一个好的算法,应该具备工作效率高,并且在系统中所占的存储量也低的特点
我们要估计一个算法的效率,大部分情况下都是度量算法工作的时间,算法的度量方法又分为以下几种
1,事后统计方法
根据设计好的程序和数据,用计时器来对不同算法的工作时间进行比较,最后得出工作效率的高低
缺点:
必须根据已经编好的程序来进行比较,如果程序本身就是错误的,那么这种方法就会很差
时间的比较依赖于计算机的型号不同和操作系统等等因素,有可能这些因素会干扰对于算法效率的判断
测试数据设计困难,例如计算十几个数据的操作比较,差异几乎没有,但是如果是成千上万的数据,差距就会很大
基于以上特点,事后统计方法不予考虑
2,事前统计方法
既然事后统计方法不予考虑使用,那么我们就使用事前统计方法来比较算法的效率,所谓事前统计方法,就是在程序编译前,依据统计方法来对算法进行估算,经过分析,我们得出程序在计算机的消耗时间取决于以下几个因素
1,算法采用的策略,方法
2,编译产生的代码质量
3,问题的输入规模
4,机器执行指令的速度
有几条因素是和算法以外的东西有关,例如编译器的选择,硬件的响应速度等等,除此之外,程序的运行时间依赖于算法的好坏和问题输入的规模,所谓问题输入的规模,也就是输入量的大小
在分析算法的运行时间时,重要的是把基本操作的数量与输入规模关联起来,基本操作的数量必须表示成输入规模的函数
函数的渐进增长
假设有两个算法,输入规模都是n,一个算法要执行2n+2次操作,另一个要执行3n+1操作,这两个算法哪个效率更高呢?其实答案并不一定,我们可以把n带入从1到n的数字看看,这两个的大小关系并不是一成不变的,在n大于1时,a的操作效率是大于b的,所以我们给出定义,输入规模在没有限制时,只要超过一个数字n,这个函数总是大于另一个函数,我们称函数是渐进增长的.
函数的渐进增长:给定两个函数f(n)和g(n),如果存在一个整数N,在n>N的情况下,f(n)恒>g(n),我们就说,f(n)的渐进增长快于g(n)
算法的时间复杂度
在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,算法的时间复杂度,也就是算法量度,记作T(n)=O(f(n)),表示随着问题规模的增大,算法执行时间的增长率和f(n)的增长率相同乘坐算法的渐进时间复杂度,简成为时间复杂度,f(n)是关于问题规模的一个函数
用大写o来表示算法时间的复杂度,我们称为大o记法,一般随着时间的增长,T(n)增长的最慢.是最优算法,算法的时间复杂度有一下几种比较主要的,像是O[1](常数阶),O[n](线性阶),O[n^2](平方阶)
如何分析一个算法的大O阶呢?
首先,我们先用1取代所有算法中出现的加法常数,在修改后的次数函数后,只保留最高阶项,并且让最高阶项前面的常数变成1,得到的就是这个算法的大O阶
说起来很容易,但是实际计算的时候,有可能算错,像是以下这几行代码
int i;
scanf("%d",&i);
printf("%d",i);
让你去计算一下大O阶,很有可能我得到的答案是3,因为执行了3次吗,但是,3只是算法的运行函数,我们推导大O阶,首先就要把常数化成1,所以这个算法的大O阶是1,也就是常数阶
如果我们把上面这行代码的printf执行10次,或者是100次,因为执行的次数和n无关,所以,我们把这种算法的时间复杂度统称为O[1],也就是常数阶(注意,常数阶必须写成O(1),里面必须是1)
像是
int i;
for(i=0,i<n,i++)这段代码的时间复杂读就是O(n),线性阶
这种
int cou;
while cou<n;
{ n=n*2;
} 这样代码的问题规模函数式是f(n)=log2 n,所以时间复杂度是O[logxN].对数阶
循环嵌套的时间复杂度一般是前者外层乘以里层,如果两个循环结构是平行结构,那么问题规模函数就是俩个时间复杂相加,当然,具体问题我们要具体分析
ps: 附上时间复杂度的表格
执行次数函数 阶 非正式术语
12 O(1) 常数阶
2n+3 O(n) 线性阶
3n^2+2n O(n^2) 平方阶
2log2n O(logn) 对数阶
2nlog2n O(nlogn) nlogn阶
3n^3 O(n^3) 立方阶
2^n O(2^n) 指数阶
所耗费的时间大小分别是 常数阶<对数阶<线性阶<nlogn阶<平方阶<立方阶<指数阶<O(n!)(阶乘阶)<O(n^n)(我也不知道是什么阶)