算法分析设计
Lecture 1 绪论
算法的五大特性:
输入、输出、有穷性、确定性(无歧义)、可行性
算法设计的一般过程
理解问题→预测所有可能地输入→在精确解和近似解中做选择→确定适当的数据结构→算法设计技术→描述算法→跟踪算法→分析算法的效率→根据算法编写代码
- 算法的描述方法
- 用自然语言描述(粗线条描述算法思想)
- 程序设计语言(能由计算机执行,但抽象性差)
- 流程图(描述简单算法)
- 伪代码(表达能力强,抽象性强,容易理解)
- 重要的问题类型
- 查找问题
- 排序问题
- 图问题
- 组合问题
- 几何问题
Lecture 2 时间复杂度
运行时间=操作时间x操作次数
语句的频度:语句重复执行的次数
渐进时间复杂度
大O复杂度
指数时间算法→多项式时间算法
大O、大Ω、大Θ复杂度:
Lecture 3 蛮力法
应用:选择排序、冒泡排序、插入排序、顺序查找、朴素的字符串匹配
蛮力法→扫描技术→遍历
蛮力法可以作为其类问题时间性能的底限,来衡量同问题的更高效算法
对于一个具有n个元素的集合,其子集数量是2^n,所以必然会导致一个Ω(2^n)的算法
组合问题:0/1背包,任务分配
图问题: 哈密顿回路、TSP问题
哈密顿回路:一个无向图,对所有顶点排列组合,哈密顿回路满足以下两个条件:(1)相邻顶点存在边 (2)首尾之间有边,复杂度为O(n!)
TSP问题:从出发点走最后回到出发点,可能的解为(n-1)!/2个
Lecture 4 问题的解空间
- 搜索的空间就是解的空间,而搜索就是在解空间里找出需要的一个,对于大部分问题来说,解空间的规模为输入规模的指数函数。
- 寻找更有效的 搜索手段 是算法不断推进的源动力(PPT里出现了无数次……那就记下来吧)
- 解空间树(Solution Space Trees,也称为状态空间树),将搜索空间组织成一棵树,从根结点到叶子结点的路径构成解空间的一个可能解,提高搜索效率。
- 解空间树是虚拟的,不需要构造一棵真正的树,只要存储从根结点到当前结点的路径。
- 现实问题难以求解的原因:
- 解空间规模太大
- 实际问题随时间而改变
- 实际问题存在很多约束条件(PPT上有分析约束条件的例题)
Lecture 5 回溯法
回溯法属于智能穷举搜索。
回溯法用 深度优先遍历 解空间树搜索解,在搜索到任意结点时先判断是否满足约束条件/是否超出函数的界(就是看这个东西符不符合条件,如果不符合就不再往下搜索)称为 剪枝(Pruning)
避免无效搜索的两种策略:
用约束条件减去得不到可行解的子树
用评估函数减去得不到最优解的子树
这两类函数称为 剪枝函数(Pruning Function)
组合问题:八皇后问题(n x n的棋盘上,n个皇后不能出现在同一行同一列同一斜线上)第i个皇后在第i行第x_i列上则:
bool Place(int k) //返回皇后k放在x[k]列会不会冲突 { for(i=1;i<k;i++) if(x[k]==x[i]||abs(k-i)==abs(x[k]-x[i]) return false; return true; } void Queue(int n) { for(i=1;i<n;i++) x[i]=0; //初始化 k=1; while(k>=1) { x[k]=x[k]+1; //在下一列放置第k个皇后 while(x[k]<=n&&!Place(k)) x[k]=x[k]+1; //搜索下一列 if(x[k]<=n&&k==n){ for(i=1;i<n;i++) cout<<x[i]; return; } else if(x[k]<=n&&k<n) k=k+1; //放置下一个皇后 else{ x[k]=0; //重置x[k],回溯 k=k-1; } } }
图问题:四色问题→何连通图可以用不多于4中颜色对每一区域着色,使相邻区域着不同颜色
图着色问题的应用:物资存储问题&会场安排问题、交通🚥问题、亚瑟王骑士宴会问题
物资存储问题:(会场安排问题同理)
有n种物资,其中两种不能放在同一间里,就把这两个点之间连一条线,连线的用不同的颜色涂色,最后顶点颜色数就是最少房间数
回溯法求解图着色问题:
/*伪代码描述 将数组color[n]初始化为0 k=1 while(k>=1) 考察顶点k的着色和其他顶点是否冲突 1.如果冲突->搜索下一颜色 2.如果不冲突->如果顶点全部着色->返回数组color[n] 2.1->如果没有全部着色->2.1.1 如果k是一个合法着色 ->k=k+1,退回while ->2.1.2 如果不是 ->重置k的颜色,k=k-1 */ /*c++描述*/ bool Ok(int k) { for(i=1;i<k;i++) if(c[k][i]==1&&color[i]==color[k])//连通且颜色一致 return false; return true; } void GraphColor(int n,int c[][],int m) { for(i=1;i<=n;i++) color[i]=0; k=1; while(k>=1) { color[k]=color[k]+1; while(color[k]<=m) if Ok(k) break; else color[k]=color[k]+1; //搜索下一个颜色 if(color[k]<=m && k==n) //成了之后输出 { for(i=1;i<=n;i++) cout<<color[i]; return; } else if(color[k]<=m && k<n) k=k+1; //处理下一个顶点 else{ color[k]=0; k=k-1; //回溯 } } }
Lecture 6 分支限界法
- 分支限界法:去掉我们明知道不可能在其中找到最优解的搜索空间(根据计算出的特定解的代价减去部分分支)
- 文字描述:
- 确定一个合理的 限界函数及界[down,up]
- 根据 广度优先 遍历问题的解空间树,估算子结点的 评估函数的可能取值
- 如果评估函数的可能取值 超出评估函数的界,就将其丢弃;否则将其加入 待处理结点 表(PT表)中
- 依次从表中选取评估函数的值取得极值的结点成为当前 扩展结点
- 重复上述过程
- 分支限界法对结点的处理是跳跃式的,所以求得最优解时无法求得最优解的各个分量,有两种方法解决这个问题:
- 对每个扩展结点保存该结点到root的路径
- 求得最优解时不断回溯到根结点,以确定最优解的分量
- 常见问题的限界函数:
0-1背包(v当前价值,w当前重量,W背包总容量,后面的比值为现在放入的物品的价值/重量比)
TSP问题:
任务分配问题:
八数码问题:(g为深度,h为不正确的数字个数)
Lecture 7 分治法(Divide-and-Conuqer)
分治法就是把一个问题划分为k个更小规模的子问题(分→治→合)
**递归( Recursion )**的两个基本要素
边界条件:确定何时终止
递归模式:大问题时如何分解为小问题的
递归的经典问题:汉诺塔问题
分治是一种思想,递归是一种手段
划分策略分为:-黑盒划分(根据问题的规模而不考虑划分对象的属性)-白盒划分(根据划分对象的特定属性值划分)
排序问题中的分治法:
- 归并排序 Merge Sort(黑盒划分)时间复杂度O(nlog n)
- 快速排序 Quick Sort (白盒划分)时间复杂度O(nlog_2n)~O(n^2)
组合问题中的分治法:
最大子段和问题
int MaxSum(int a[],int left,int right) { sum=0; if(left==right) //序列长为1 { if(a[left]>0) sum=a[left]; else sum=0; } else{ center=(left+right)/2; //划分 leftsum=MaxSum(a,left,center); rightsum=MaxSum(a,center+1,right); s1=0;lefts=0; for(i=center;i>=left;i--) { lefts+=a[i]; if(lefts>s1) s1=lefts; } s2=0;rights=0; for(j=center+1;j<=right;j++) { rights+=a[j]; if(rights>s2) s2=rights; } sum=s1+s2; //计算情况3的最大子段和 if(sum<leftsum) sum=leftsum; if(sum<rightsum) sum=rightsum;//合并 } return sum; }
棋盘覆盖问题
循环赛日程安排问题
Lecture 8 贪心法
贪婪算法:一步一步地构建问题的最优解决方案,其中每一步只需要考虑眼前的最优选择(通过局部最优达到全剧最优),大多数情况能达到近似最优解。
贪心算法要获得最优解的 条件:
- 最优子结构性质(一个问题的最优解包含其子问题的最优解)
- 贪心选择性质(问题的整体最优解可通过局部最优的选择得到)
TSP问题的贪心策略:
策略(1): 从一个顶点出发每次都选择此点出发的最近的点
时间复杂度O(n^2)
策略(2):在整个图中选择剩下的最短边,最终形成哈密顿回路
时间复杂度O(nlog_2n)
图着色问题的贪心策略
先从顶点开始,用颜色1着色,然后在图中找还能用颜色1着色的点上色,没有了之后再用颜色2从未着色的第一个点开始着色,♻️,以此类推。
伪代码: 1. color[1]=1; //把第一个点上1色 2. for(i=2;i<=n;i++) //把其他顶点都初始化 3. k=0; 4. 循环(所有顶点都上色时退出循环) 4.1 k++; //取下一个颜色 4.2 for(i=2;i<=n;i++) //用k色为所有可以的点着色 4.2.1 如果vertice i已经着色,跳转4.2,看下一顶点 4.2.2 如果不冲突可以着色 则color[i]=k; 5. 输出k;
最小生成树问题 Minimal Spanning Trees
(计算机互相连线组网问题)
两种贪心策略:
(1) 最近顶点策略 :任选一个顶点,把最近的顶点添加到生成树中,即Prim算法
(2) 最短边策略:在所有边中找最短的边加入🌲中,即Kruskal算法
组合问题中的贪心算法:
- 背包问题(三种策略:价值最大、重量最轻、单位重量价值最大)
- 多机调度问题:最长处理时间作业优先
Lecture 9 动态规划法
- 斐波那契数列 1 1 2 3 5 8 13 21 …… 其中的任意一个数都叫斐波那契数。
- 动态规划=贪婪策略+递推(降阶)+存储递推结果
- 动态规划法求解的问题的具体特征:
- 能够分解为相互重叠的若干子问题
- 满足最优性原理(同最优子结构性质)(见上面的算法)