算法
一个有限指令集
接受一些输入(有些情况下不需要输入)
产生输出
一定在有限步骤之后终止
每一条指令必须有充分明确的目标,不可以有歧义
计算机能处理的范围之内
描述应不依赖于任何一种计算机语言以及具体的实现手段
算法的5个基本特征:输入、输出、有穷性、确定性和可行性
算法设计的要求
- 正确性
- 可读性
- 健壮性
- 时间效率高和存储量低
例如:选择排序算法的伪码描述
voidSelectionSort( intList[], intN )
{ /* 将N个整数List[0]...List[N-1]进行非递减排序*/
for( i = 0; i < N; i ++ )
{
MinPosition= ScanForMin( List, i, N–1 );
/* 从List[i]到List[N–1]中找最小元,并将其位置赋MinPosition
*/ Swap( List[i], List[MinPosition] );
/* 将未排序部分的最小元换到有序部分的最后位置*/
}
}
抽象------
List到底是数组还是链表(虽然看上去很想数组)?
Swap用函数还是用宏去实现?
算法效率的度量方法
我们如何来度量一个算法的执行时间呢?
- 事后统计方法
- 事前分析估算方法:在计算机程序编写前, 依据统计方法对算法进行估算
经过总结,一个高级语言编写的程序在计算机上运行时所消耗的时间取决于下列因素:
* 算法采用的策略,方案
* 编译产生的代码质量
* 问题的输入规模
* 机器执行指令的速度
由此可见,抛开这些与计算机硬件、软件有关的因素,一个程序的运行时间依赖于算法的好坏和问题的输入规模(所谓的问题输入规模是指输入量的多少)
研究算法的复杂度,测重的是研究算法随着输入规模扩大增长量的一个抽象,而不是精确地定位需要执行多少词,因为如果这样的话,我们就又得考虑编译器优化等问题。
抽象:不关心编写程序所用的语言是什么,也不关心这些程序将跑在什么样的计算机上,我们只关心它所实现的算法。不计那些循环索引的递增和循环终止条件、变量声明、打印结果等操作。最终,在分析程序的运行时间时,最重要的是把程序看成是独立于程序设计语言的算法或一系列步骤。在分析一个算法的运行时间时,重要的是把基本操作的数量和输入模式关联起来。
函数的渐进增长:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐进快于g(n)。
什么是好的算法?
空间复杂度S(n)----根据算法写成的程序在执行时占用存储单元的长度。这个长度往往与输入数据的规模有关。计算公式记作:S(n)=O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断。
时间复杂度T(n)----根据算法写成的程序在执行时耗费时间的长度。这个长度往往也与输入数据的规模有关。在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。时间复杂度过高的低效率算法可能导致我们在有生之年都等不到运行结果。算法的时间复杂度,也就是算法的时间量度,记作:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。这样用大写O()来体现算法时间复杂度的记法。我们称之为大O记法。一般情况下,随着输入规模n的增大,T(n)增长最慢的算法为最优算法。
--用常数1取代运行时间中的所有加法常数。
--在修改后的运行次数函数中,只保留最高阶项
--如果最高阶项存在且不是1,则去除与这个项相乘的常数。
--得到的最后结果就是大O阶
常数阶,线性阶,平方阶,对数阶( 2 x 2^x 2x=n,得到x= l o g 2 n log_2^n log2n,底数2一般不写,省略)
在分析一般算法的效率时,我们经常关注下面两种复杂度
最坏情况复杂度 T w o r s t ( n ) T_{worst}(n) Tworst(n)
平均复杂度 T a v g ( n ) T_{avg}(n) Tavg(n)
\qquad T a v g ( n ) T_{avg}(n) Tavg(n) ≤ \leq ≤ T w o r s t ( n ) T_{worst}(n) Tworst(n)
复杂度的渐进表示方法
T(n)=O(f(n))表示存在常数C>0, n 0 n_0 n0>0使得当n ≥ \geq ≥ n 0 n_0 n0时有T(n) ≤ \leq ≤C . ^. .f(n)
T(n)= Ω \Omega Ω(g(n))表示存在常数C>0, n 0 n_0 n0>0使得当n ≥ \geq ≥ n 0 n_0 n0时有T(n) ≥ \geq ≥C . ^. .g(n)
T(n)= Θ \Theta Θ(h(n))表示同时有T(n)=O(h(n))和T(n)= Ω \Omega Ω(h(n))
复杂度分析小窍门
若两段算法分别有复杂度 T 1 T_1 T1(n)=O( f 1 f_1 f1(n))和 T 2 T_2 T2(n)=O( f 2 f_2 f2(n)),则
\qquad T 1 T_1 T1(n)+ T 2 T_2 T2(n)=max(O( f 1 f_1 f1(n)),O( f 2 f_2 f2(n)))
\qquad T 1 T_1 T1(n)X T 2 T_2 T2(n)=O( f 1 f_1 f1(n)X f 2 f_2 f2(n)))
若T(n)是关于n的k阶多项式,那么T(n)= Θ \Theta Θ( n k n^k nk)
一个for循环的时间复杂度等于循环次数乘以循环体代码的复杂度
if-else结构的复杂度取决于if的条件判断复杂度和两个分支部分的复杂度,总体复杂度取三者中最大。