尽管直观,适用范围广,但枚举,回溯等暴力方法常常无法走出低效的阴影
越是通用的算法,越不能深入挖掘问题的特殊性
本章介绍一些经典问题的高效算法,由于是量身定制,这些算法从概念思路到程序实现都是千差万别的
本章开始,读者刚刚开始接触严肃的算法设计理论
算法分析初步所需要解决的问题就是在写程序之前按估计程序的时空开销,并作出决策,如果算法又复杂速度又慢,就可以先缓缓,等下写
8.1.1 渐进时间复杂度
最大连续和问题
最简单的方法就是枚举,得出如下程序
tot = 0; best = A[1];//初始化最大值 for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { //检查连续子序列A[i],...,A[j] int sum = 0; for(int k = i; k <= j; k++) { sum += A[k]; tot++; }//累加元素和 if(sum > best) best = sum; //更新最大值 }
注意这边best的初值是A[1],这是最保险的做法,不要写best=0,因为未必序列中存在非负数,可能全都是负数
这边计算的tot与机器的运行速度无关,不同机器的速度不一样,运行时间也会有差异,但tot值一定相同。换句话说,它去掉了机器相关的因素,只衡量算法的工作量大小,依旧是加法操作的次数
统计程序中“基本操作”的数量,可以排除机器速度的影响,衡量算法本身的优劣程度
在本题中,将加法操作作为基本操作,类似地可以把其他四则运算,比较运算作为基本操作,一般不会严格定义基本操作的类型,而是根据不同情况灵活处理
可以计算得出输入规模为n时,其加法操作次数为T(n) = n(n+1)(n+2)/6
上面的式子在n很大的情况下,平方项和一次项对式子的影响不大,也就是T(n) = O(n^3)
这二者是同阶的,具体可以参考高数的同阶无穷大的定义
同阶的含义简单来说就是增长情况相同,我们可以只保留最大项,并忽略其系数,得到的简单式子称为算法的渐进时间复杂度(asymptotic time complexity)
基本操作的数量往往可以写成关于输入规模的表达式,保留最大项并忽略系数后的简单表达式称为算法的渐进时间复杂度,用于衡量算法中基本操作数随规模的增长情况