算法分析与设计复习
算法分析与设计复习
2016年初,研一上学期期末考试前,复习并总结算法分析与设计科目的内容。复习过程参照《算法导论》中文第2版,同时参照PPT,章节划分根据PPT内容
概要:
第一章 概述
第二章 插入排序&分治策略
第三章 复杂度分析
第四章 堆与堆排序
第五章 快速排序
第六章 线性时间排序
第七章 中位数和顺序统计
第八章 动态规划(DP)
第九章 贪心算法
第十章 最短路径算法
第十一章 回溯法&分支限界法
第十二章 NP问题与一些概念
第一章 概述
-
算法的应用范围
算法在诸如生物等诸多领域有其应用
-
算法的意义
算法在很多情况下让不可能完成的事情变成了可能,让处理的很慢的过程变快。
-
一个铺垫
一串不全为负的数,怎么取能拿到一段和最大的子串(最大子段和)
第二章 插入排序&分治策略
-
插入排序
- 掌握插入排序的方法
-
循环不变式的证明
初始化:
保持:
终止:
-
逐步计算插入排序的时间复杂度:
结论(基于RAM模型下):
Best Case:O(n)
Worst Case:O(n^2)
-
分治策略
-
概述:
大问题拆成多个小问题,分别计算每个小问题的解最后把多个小问题合成原问题的解
-
-
MERGE Sort
- 掌握归并排序的方法
-
分析归并排序
- 通过递归式分析
- Best case:nlgn
- Worst case:nlgn
-
课后习题总结
- 推演一个插入排序的过程
- 循环不变式证明线性查找的正确性
- 对给定的时间复杂度表达式写出Θ
- 推演一个MERGE Sort
- 算法设计题:可以看*
第三章 复杂度分析
-
时间复杂度分析
-
归并排序分析
-
代换法(证明手段)
- 做一个好的猜测
- 假设这个界对某一个范围内成立
- 带入得出结论
- 数学归纳法证明结论正确
-
递归树
- 时间复杂度的表达式是每一层拆分开销之和加上最底层每一个未知的开销。
-
主定理(必会)
- 形如:T(n)=aT(n/b)+f(n)
-
三种情况:
- 若存在ε>0,有f(n)=O(n^(log b(a)-ε)),则T(n)=Θ(n^(log b(a)))
- 若f(n)=Θ(n^(log b(a))),T(n)=Θ(n^(log b(a))lg(n))
- 若对某ε>0,有f(n)=Ω(n^(log b(a)+ ε)),且对常数c<1与所有足够大的n,有af(n/b)<=cf(n),则T(n)=Θ(f(n))
-
-
必会技能
- 递归式画递归树
-
课后习题总结
-
代换法证明时间复杂度
- 猜测渐进确界,证明之
- 主方法可用情况的练习
- 判断主方法是否可用,确定渐进上界
-
第四章 堆与堆排序
-
堆的概念
- 利用树的数组存储方式,PARENT(i) = i/2, LEFT(i) = 2i, RIGHT(i) = 2i + 1
-
两个关键的函数
- MAX-HEAPIFY 已有堆的情况下,加入新结点后调整堆以使得其满足堆的条件。O(lgn)
- BUILD-MAX-HEAP 基于无序数组调整为大顶堆Θ(n)
- 其中,BUILD-MAX-HEAP是由(n/2)向下取整次MAX-HEAPIFY组成的,但其时间复杂度是Θ(n)
-
堆排序时间复杂度
- 时间复杂度由两部分组成,1次BUILD-MAX-HEAP,n-1次MAX-HEAPIFY,故结果为T(n)=Θ(n)+nO(lg n)=O(nlgn)
- 最好的情况下,时间复杂度为O(n)
- 优先级队列
-
必会技能:
- 区分一个数组是不是堆
- 手工跑堆排序或者建堆过程
-
课后习题总结:
- 区分一个序列是否为最大堆
- 画出一个完整的MAX-HEAPIFY流程
第五章 快速排序
-
快速排序
-
时间复杂度
- 最好Θ(nlgn)
- 平均Θ(nlgn)
- 最坏Θ(n2)
- 知道分治的过程————以一个数做分治,分成两组
- 掌握快排的手动做法
- 有一个关于每次都从1/10位置分隔的情况下仍为nlgn的证明
-
-
随机快排
- 关键:用随机来避免出现Worst case
- 实际表现是最快的排序
-
时间复杂度
- 平均Θ(nlgn)
- 最坏Θ(n2) (仅在理论上存在)
-
课后习题总结
- 若快排数组中元素都一样,时间复杂度是多少?
- 答:这是Worst Case 所以是Θ(n2)
第六章 线性时间排序
- BDD证明基于比较排序最坏情况的最小量级是nlgn
-
计数排序
- 计数排序是一种稳定排序
- 时间复杂度Θ(k+n) ,在k与n同一数量级时,该复杂度为O(n)
-
关键:被排序的n个数在0~k之间
- 范围不能超过数的个数的量级
- 若这n个数的范围是n^2,则时间复杂度不再是O(n),而是O(n^2)
-
掌握计数排序的完整过程,可以手跑
- 创建一个容量为k的数组C,并将所有元素置零
- 遍历原数组A,在C中对应的位置+1
- 遍历数组C,从第2个元素开始的所有元素的值依次用前一个元素与当前元素的和替换。
- 逆序遍历A,拿到元素a,在C中查找其位置(即C中元素对应的数值)在数组B中的对应位置放入a即可。同时将C中的那个数据-1
-
基数排序
- 掌握基数排序的完整过程,可以手跑
- 基数排序的特点:利用稳定排序的特点,从低位到高位按位排序
-
时间复杂度
- Θ(d T(n))
- 在每位计算使用计数排序的时候,时间复杂度为Θ(n)
-
桶排序
- 掌握桶排序的完整过程,可以手跑
- 主要针对均匀分布的数,利用哈西,靠散列的过程完成排序
- 要点:类似于计数排序,为待排序数组提供若干个区间,将各个待排序的数放进区间内,完成区间内排序的同时,就得到了一个完整的排序结果。
-
算法小结
- 区分原地排序
- 区分稳定排序
-
课后习题总结
- 推演一个计数排序的过程
第七章 中位数和顺序统计
- 找到最大值/最小值的方法:找到最值Θ(n),从头到尾遍历,每一步记下当前的最值,结果就是最后的结果
-
查找第i个元素的随机算法
- 存在Worst Case可以在线性时间完成计算的算法
-
了解PARTITION函数的作用及原理
- 该函数利用随机数,拿到一个区间内的某数a,运行一次partition后,能保证以a为界将原区间分为两个部分,选这两个部分中的一部分继续partition
- 优化后的partition主要在随机拿到划分元素上下功夫,通过将所有元素5个一组分成小组,组内找出中位数,用所有的中位数再找出中位数a后,以a为界划分两个部分,不断循环,这样能保证划分的质量。
第八章 动态规划(DP)
-
动态规划问题的四个基本步骤
- 确定最优解的结构。(DP的必要条件)
- 递归定义最优解的值。
- 自底向上计算解的值。
- 利用计算得到的信息构造最优解。
-
装配线调度问题
- 关键:问题的最优解包含其子问题的最优解
-
递推关系式
-
矩阵链乘
- 关键:拆分后的矩阵链也要以最少的计算次数才能得到正确的结果
-
递推关系式
-
示意图
-
DP问题的要素:最优子结构
- 有向图中两节点的最短路径长度是有最优子结构的
- 有向图中两节点的最长路径长度没有最优子结构
- 最短路径长度与最长路径长度的区别是是子问题是否独立。
-
自上而下与自底向上之间的比较
-
自上而下的递归形式算法
- 可能会存在重复计算的问题
- 有递归的时间开销
-
自底向上的动态规划算法
- 没有重复计算,但可能存在多余计算,即计算并不会用到的数据
-
-
备忘录以及应用备忘录后两种形式算法的对比
- 备忘录通过记录计算过的子问题,避免了在自上而下进行递归计算时,重复计算的问题
-
自底向上的动态规划方法
- 需要计算所有情况
- 调用方式节省开销
-
自上而下+备忘录
- 只求解需要求解的子问题
- 递归调用产生多余的开销
-
最长公共子序列(Longest common subsequence/LCS)
- 暴力算法:查找所有子串,时间复杂度是指数级
-
DP求解LCS原理
- 两字符串A(m)、B(n),若最后一个字符相同则一定在其某个最长公共子序列C内,故C的长度为1+LCS(A(m-1),B(n-1))
- 两字符串A(m)、B(n),若最后一个字符不相同,则C的长度为max{LCS(A(m-1),B(n)),LCS(A(m),B(n-1))}
- 由于上两条,故LCS由AB某一子字符短的LCS组成,故具有最优子结构
-
DP求解LCS图解
-
图解的解释:
- 用mn的空间记录Ap,Bq时的LCS,从短到长记录
-
从第一行开始一直到最后一行,查看每个格子所对应的横纵字母
- 相同则取其左上方结果+1
- 不相同择其上方或左方大者放入格子
-
最大子段和
-
DP求解最大子段和的原理
- 最大子段和一定是某个元素a加上了前面的某一个子段和或者是a元素本身。
-
DP求解数组A最大子段和过程
- B[0]=A[0]
- B[1…n]=max{B[1…n], B[0…n-1]+ B[1…n]}
- 遍历B,其中的最值就是最大子段和
-
-
课后习题总结
- 修改原算法使其能顺序输出装配结果
- 推演一个矩阵链乘的最优加括号方式
- 推演两个字符序列的LCS
第九章 贪心算法4
-
贪心策略
- 贪心选择性质:贪心策略希望通过局部选择最优达到全局最优的目的。但是并不是所有情况都能达到全局最优。
-
活动选择问题
-
活动选择问题可以用动态规划求解,活动选择问题存在最优子结构,如下:
-
活动选择问题中的一个关键定理:对于任意子问题Sij,令am为Sij中结束时间最早的活动:
- 证明:若在最优解中,存在an为第一个活动,且an在am后结束,则an一定可以用am替换之。
- 由于定理带来的特殊性质——贪心选择性质,所以可以用贪心算法求解
- 贪心求解活动选择问题:从头开始不断选出当前剩余活动中,满足开始时间且结束时间最早的活动。
-
-
背包问题
包含0-1背包、分数背包两个子问题
- 讨论目的:从两种背包问题中对比动态规划与贪心算法
-
异同点
- 共同点:均有最优子结构,符合动态规划问题的求解条件
-
不同点:针对同一物品确定是否选择情况
- 分数背包:性价比最高的,一定在最优子结构中,因为其他的物品都达不到这种评价标准
- 0-1背包:不确定,需要对所有情况予以讨论
-
解决策略:
-
分数背包:贪心算法
- 具体方法:把当前性价比最高的物品装到不能再装(没有了或者包满了)为止
-
0-1背包:DP
-
具体做法:刻画最优子结构,标准DP做法
-
-
-
哈夫曼编码
利用变长编码使编码长度大大减小,原理:频率越高的字符,表示的编码越短。
第十章 最短路径算法
最短路径算法包含单源最短路径、每对结点间的最短路径。。。。
-
关于最短路径概念的讨论
- 最短路径满足最优子结构,即:一个最短路径a中的任意两点的最短路径b一定在a的路径中
- 三角不等式:对于所有的 u,v,x∈V, 以下不等式成立 δ(u,v) ≤δ(u,x)+δ(x,v)
- 含有负环的图中,某些最短路径可能不存在5
- 单源最短路径:求解从一个点到其他所有点的最短路径的集合
-
狄杰斯特拉算法(单源)
狄杰斯特拉算法本质上利用了贪心思想,不断的更新当前最短路径,用来处理不含负边的图
- 时间复杂度:O(E+VlgV)
-
算法流程:
- 把原点a加入图中。
- 记录所有从原点能到达的结点的路径长度,其他的记为∞
-
重复以下几步,直到所有节点都记录在图中
- 选出未记录到图中的路径v最小的那个结点p
- 把结点p加入图中
- 用vp+p到其他结点的路径长度与原记录中的最短路径对比,记下小的那个
-
无权图的最短路径算法(单源)
无权图由狄杰斯特拉算法退化而来,通过广度优先的的搜索模式记录最短路径
-
Bellman-Ford算法 (单源)
Bellman-Ford算法可以处理含有负边、负环的图,如果存在负环,则能报告由环为负环
-
每对结点间最短路径
- V次Dijkstra:O(VE+V^2lgV)
-
V次Bellman-Ford:O(V^2E)
- 最坏情况(稠密图):O(V^4)
-
每对结点间最短路径第一种算法(矩阵“乘”)
-
每对结点间最短路径第二种算法(Floyd)
- 概括:从1个点开始不断往图中增加点,如果新加入的点能够对当前的最短路径产生影响的话,就更新当前的最短路径结果表;约束过哪些结点
-
递归式:
-
流程:
- 初始化一个矩阵存储方式的V个边的图
-
循环下列步骤V次,记当前次数为k
- 对每一条路径判断,被k分为两段的该路径是否会得到更小的结果,用较小值更新它
-
每对结点间最短路径第三种算法(Johnson)
- 概括:为了利用Dijkstra算法,于是通过更改权值,消除负权值以满足Dijkstra。因为Dijkstra代价小
-
课后习题总结
- 推演一个Bellman-Ford算法
- 推演一个矩阵“乘”算法
- 推演一个Floyd算法
第十一章 回溯法&分支限界法
回溯法和分支限界法都是搜索搜索方式,当DP、贪心这些方法没有办法使用时,只能通过搜索来找到结果。搜索的方法主要包括这两类。回溯法是一种深度优先的搜索方式
-
n皇后问题(回溯法)
- 解空间具有n!个n元组组成
-
算法步骤:
-
假设第一个在x的位置,x从0~n
-
假设第二个在y的位置,y从0~x-1,x+1~n
-
假设第二个在y的位置,z从0~n(不含x、y)
- ……
-
-
-
- 注意限界函数:不与其它皇后冲突
-
15迷问题(分支限界法)
- 15迷问题有一半的初始状况无解
- 用回溯法的情况下,很大的几率是浪费时间,没有意义
-
采用LC-search(最小代价least cost)的15迷解法
- 对每一个分支进行打分,衡量其计算过的次数,以及其距离目标结果的差距个数。
- 选择当前最好的那一个分支进行展开
- 循环以上操作,直到找到结果
-
子集合数(回溯法)
- 限界函数:既不超过目标数,剩下的数字和还大于距目标数的差距
-
01背包(回溯法)
- 限界函数:还有可能(通过分数背包计算剩下的产品价值)产生比当前的最优解好的情况才继续搜索
-
回溯法的一般方法
- 递归调用
- for循环调用
第十二章 NP问题与一些概念
-
可计算性
- 不是所有问题都是可计算的
- 普斯特竞赛问题(不可求解)
- 有限步操作确定一个给定的多元多项式是否有整数根问题(不可求解)
-
停机问题(不可求解)
- 证明(类似悖论)
-
NP完全
- P:在确定化的图灵机上可在多项式级计算时间内求解的可判定问题集合。
- EXP:在确定化的图灵机上可在指数级计算时间内求解的可判定问题集合。
- NP:存在有效验证算法的可判定问题集合
-
NPC:NP集合中具有如下性质的问题:若该问题可被有效求解,则其求解方法可被用于有效求解其它NP完全问题。
- TSP:找到能否在距离D内访问N个城市
- 三色问题:用三个颜色为地图上色,要求邻国颜色不一样
- SAT:电路可满足性
-
如何面对NPC问题?如果一个问题是NP完全问题,则我们所设计的算法最多能同时做到以下三点中的两点:
- 求解问题的最优解
- 在多项式级时间内求解
- 求解问题的任一实例
- O,my god——助记,上帝、上界 ↩︎
- 欧米伽——助记,家在地上,下界 ↩︎
- θ的中间的一助记,那个横足以说明是中间的啊~ ↩︎
- 个人理解,贪心算法能从动态规划演变成贪心,主要原因是,有一个准确的原因确定当前哪一个元素一定进入最优解 ↩︎
- 不是全部,因为,有些点之间不经过那个负环 ↩︎
- 原理,若有n个结点,则最短路径最多在经过所有节点,也就是n-1个边后得到最小值,否则说明有负环 ↩︎
- 这个过程称为松弛,网上表示对此名称有些疑问 ↩︎
- 一个图的二阶矩阵表示,其中对角线元素为0(表示自己到自己的距离为0),其余都是∞。幺元矩阵用来表示经过0个节点,图中点到所有顶点的距离 ↩︎
- 只是借用说法,并不是一个乘法 ↩︎