1. 计算
学习DSA的目的是实现有效的和高效的计算,同时在资源消耗的方面做到足够的低廉。
计算 = 信息处理:借助某些工具,遵照一定规则,以明确而机械的形式进行。
计算模型 = 计算机 = 信息处理工具
算法:在特定的计算模型下,旨在解决特定问题的指令序列。
算法的要素:
输入 |
待处理的信息(问题) |
输出 |
经处理的信息(答案) |
正确性 |
的确可以解决指定的问题 |
确定性 |
任一算法都可以描述为一个由基本操作组成的序列 |
可行性 |
每一基本操作都可实现,且在常数时间内完成 |
有穷性 |
对于任何输入,经有穷次基本操作,都可以得到输出 |
…… |
…… |
好算法的要素:
正确 |
符合语法,能够编译、运行 |
健壮 |
能辨别不合法的输入并做适当处理,而不致非正常退出 |
可读 |
结构化 + 准确命名 + 注释 |
效率 |
速度尽可能快,存储空间尽可能少 |
2. 计算模型
算法分析的两个主要方面:
1. 正确性:算法功能是否与问题要求一致。
2. 成本:运行时间 + 所需存储时间。
如何度量成本?
1.特定算法应用于不同实例:
问题:同一问题等规模的不同实例,计算成本不尽相同,甚至有实质差别。
例如:在平面上的n个点中,找到所形成三角形面积最小的三个点。以蛮力算法为例,最坏情况下需枚举C(n,3)种情况,运气好时只需要1次。
结果:为了稳妥起见,在规模相同的所有实例中,只关注最坏(成本最高)者。
2.特定问题使用不同算法:
问题:实验统计是最直接的方法,但无法准确的反应算法的真正效率。
例如:不同的算法,可能更适应于不同规模的输入。
不同的算法,可能更适用于不同类型的输入。
同一算法,可能由不同的程序员、用不同的程序语言、经不同的编译器实现。
同一算法,可能实现并运行于不同的体系结构、操作系统……
结果:为了给出客观的评判,需要抽象出一个理想的平台或模型。不再依赖于上述种种具体的因素,从而直接而准确地描述、测量并评价算法。
TM(TuringMachine)和RAM(Random AccessMachine)
TM模型和RAM模型是一般计算工具的简化与抽象,使我们可以独立于具体的平台,对算法的效率做出可信的比较和评判。
在这些模型中,算法的运行时间 ∝ 算法需要执行的基本操作次数,T(n) = 算法为求解规模为n的问题,所需执行的基本操作次数。
3. 大O记号(big-O notation)
渐进分析:当问题规模足够大后,通过计算成本如何增长,可以得到这样一条曲线:
但是我们所关心的并不是这条曲线局部的、细微的、暂时的一些趋势,而是看它主要的、长远的变化趋势。
为此可以采用所谓的大O记号从形式上简化成本:
T(n) = O(f(n)) iff ∃ c> 0 ,当 n >> 2后, 有 T(n) < c * f(n)
与f(n)相比,T(n)更为简洁,但依然反映了前者增长的趋势。
渐进分析还有其他记号,比如大Ω记号:
T(n) = Ω(f(n)) iff ∃ c > 0 ,当 n >> 2后, 有 T(n) > c * f(n)
大Θ记号:
T(n) = Θ(f(n)) iff ∃ c1 > c2 > 0 ,当 n >> 2后, 有 c1 * f(n) > T(n)> c2 * f(n)
大Ω记号表示函数的渐进下界,大Θ记号表示函数的渐进确界。
大O记号的两条性质:
(1)对于任意常数c > 0,有O(f(n))= O(c * f(n))
(2)对于任意常数a > b > 0,有O(na + nb) = O(na)
通过大O记号对算法进行度量,常见复杂度有:
复杂度增长速度对比:
4. 算法分析
两个主要任务= 正确性(不变性 x 单调性)+ 复杂度
C++等高级语言的基本指令,均等效于常数条RAM的基本指令。在渐进意义下二者大体相当。
复杂度分析的主要方法:
1.迭代:级数求和
2.递归:递归跟踪 + 递推方程
3.猜测 + 验证
级数复杂度:
算术级数 |
与末项平方同阶 |
幂方级数 |
比幂次高出一阶 |
几何级数 |
与末项同阶 |
收敛级数 |
O(1) |
几何分布 |
O(1) |
调和级数 |
Θ(logn) |
对数级数 |
Θ(nlogn) |
5. 迭代与递归
减而治之:为求解一个大规模的问题,可以将其划分为两个子问题:其中一个退化平凡,另外一个规模缩减。通过分别求出子问题的解得到原问题的解。
分而治之:为求解一个大规模的问题,可以将其划分为若干(通常两个)子问题,规模大体相当。通过分别求出子问题的解得到原问题的解。
注:第一次写笔记加上第一章基本上都是基础知识感觉好多都是直接搬的教材,希望后边能好点吧。本课为清华大学邓俊辉老师的数据结构课程,MOOC平台为学堂在线,开课时间是2014年的9月15日。如果感兴趣的同学可以去看看,个人觉得还是讲的相当好的。