本文仅仅是为了快速理解掌握多个经典算法类型而总结的思想性概要,并无算法实现。
1. 递归
递归算法的经典实例为汉诺塔问题和全排列问题,在此对汉诺塔问题简单总结一下。
假设f(n-1)已经解决,如何解决f(n)问题,也就是说假设n-1个圆盘的顺序已经安放正确,如何正确安放第n个圆盘, 使得所有n个圆盘安放正确。
Hanoi(n,A,B,C)
if n=1
将圆盘从A移动到C
else
Hanoi(n-1,A,C,B)//借助C,将n-1个圆盘从A移动到B
将第n个圆盘移动到C
Hanoi(n-1,B,A,C)//借助A,将剩下n-1个圆盘从B移动到C
具体过程可以根据伪码画出递归树,并写出递归方程,进行算法分析。
2. 分治
分置即分而治之。主要分为三个步骤:划分、治(解决)、合并。经典应用有合并排序,快速排序和矩阵乘法等。
A 、 合并排序:将整个数组中的所有元素进行多次划分,比如使用二分,直到划分成一个个独立的元素,此为 分;然后每两个元素进行比较并重新排序,此为治;将重新排序的元素再逐次合并成整个数组,此为合。
MergeSort(A,p,r)
if p<r
q=(p+r)/2 //找划分点
MergeSort(A,p,q) //划分
MergeSort(A,q+1,r) //划分
Merge(A,p,q,r) //治(排序)+合并
B、快速排序:取第一个元素为标记元素,从后往前,依次与每个元素比较,直到一个比自己小的元素a,此时 将该元素放到标记元素的位置上,即覆盖第一个元素,再从前到后依次与每个元素比较,直到找到一个比自 己大的元素b,此时将该元素覆盖a,再从后往前进行类似的过程,覆盖b,最后的一个元素用标记元素覆盖 上,这里要注意的是每次划分的标记元素都是不改变的,此时标记元素就有一个最终的位置p,p前面的元素 都比标记元素小,后面的都比它大。然后再对p的左边和右边分别进行快速排序即可。
QuickSort(A,p,r)
if p<r
q=Partition(A,p,r)
QuickSort(A,p,q-1)
QuickSort(A,q+1,r)
3. 动态规划
动态规划利用了分治的思想,将问题分成很多个子问题,自底向上迭代求解每个子问题的解并记录子问题的解,最后合并成整个问题的解。这样在求解子问题的时候直接查表,就不会重复求解了。典型的问题有装配线调度,矩阵连乘,0/1背包,最长公共子序列,最优二叉搜索树等问题。实质上就是填表的过程。
A、装配线调度:从初始点开始向终止点计算,装配线1上的某一点的代价有两种可能值,第一种为由本条装配线的前一点直接过渡到这个点的值m=本条装配线上一步代价+本次代价,第二种为由装配线2转移得到的值n=装配线2上一步代价+转移代价+本次代价;同理装配线2的某一点的代价也有两种可能值,求法与前面类似;每次扩展一个点就比较一下m和n的值,选择小的即为当前点的调度方案,当从头到尾每个点都扩展之后就能得到装配线调度问题的完整解。
B、矩阵连乘:解决这个问题需要一个三重循环,计算矩阵序列的每个可能的分段点的左边序列的连乘代价和右边序列的连乘代价,再加上二者和分段点连乘的代价即为这种分段方法的总代价,每次循环都选择当前分段方法的最小代价,最后就能得出最小代价的分段方法。
C、0/1背包:在装物品时有两种选择,第一种是容量不足,无法装,则最优选择为当前状态;第二种是容量充足,选择装与不装,若装,则求得一价值m,若不装,则为当前价值n,比较m,n并填表。
4. 贪心算法
贪心算法在大多数情况下能得到最优解,即使找不到,其近似解也让人满意。思想就是每一步都选择最优决策。主要应用为任务选择,背包问题等。
A、任务选择问题:任务选择问题就是从一系列的任务中选择一个任务序列满足时序的情况下而任务数最多。首先对任务依照完成时间进行排序,每次选择完成时间最小的任务加入序列,直到不满足总时间的限制,即得到任务数最多的任务序列。
B、背包问题:即可分割的背包问题。对每个物品的单位重量从大到小排序,每次选择单位重量最大的物品直到背包装满为止(物品可分割)。