概括
策略 | 时间复杂度 | 空间复杂度 | 思想 | 特点 |
分治法 | O | O(n) | 将问题拆解,递归求解后合并 | 归并怕徐、二分查找、快速排序 |
动态规划 | O( | O(n)~O( | 记录子问题 解避免重复,逐步构建最优解 | 自顶向下、自底向上 |
贪心 | O | O(1) | 每一步都选择当前局部最优解 | 最短路径 |
回溯 | 指数级 | O(n) | 试探性找解,遇到死路退回 | 探索性遍历空间 |
分支界限 | 指数级 (优化后) | O(n) | 广度优先 |
分治法
顾名思义分治法的思想就是分而治之,将原有大的问题进行拆分,拆分成若干个与原问题结构相同、形式相同、互相独立、规模较小的子问题,递归的解决这些子问题,然后再将各个子问题进行合并得到原问题的解。
时间复杂度:
对原问题的要求
分治法的使用对原问题有所要求。
原问题规模缩小到一定程度就可以容易解决
原问题可以分解为若干个规模较小的相同问题
利用该问题分解出的子问题的解可以合并为该问题的解
该问题所分解出的各个子问题是相互独立的
分治法应用-递归
递归就是在运行的过程中调用自己。
递归调用(嵌套调用)时,越晚被调用的函数越早返回结果,栈的先进后出恰好满足此类属性。
例:
下表中第0项的值为1;第1项的值为2;第2项的值为第0项+第1项;第3项的值为第1项+第2项;以此类推。
第几项 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
数值 | 1 | 2 | 3 | 5 | 8 | 13 | 21 |
可以写为
int F(int n)
{
if (n=0) return 1;
if (n=1) return 2;
if (n>1) return F(n-1)+F(n-2);
}
此时如果想要输出第6项则计算方式是:
因此在程序中是
先用 if (n>1) return F(n-1)+F(n-2);
将F(6)分为了F(5) + F(4);
再次调用 if (n>1) return F(n-1)+F(n-2);
将F(5)分为了F(4) + F(3);F(4)分为了F(3) + F(2);
以此类推,直到将其分解为可以直接返回的F(1)或F(0)
运行过程中不断调用自身此为递归
分治法应用-二分法
Search(L,a,b,x) //L表示待查数组,a表示数组L初始下标,b表示数组L结束下标,x表示需要查找的值
{
if(a>b) return (-1); //a>b表示一直查找到数组L的最后或最前都未找到需要查找的值
else{
m=(a+b)/2; //首先取数组L中间下标对应的值进行查找
if(x==L[m]) return(m); //如果中间值恰巧为需查找的值则直接返回
else if(x>[m]) //如果需要查找的值>中间值
return (Search(L,m+1,b,x);
//将下标为m+1的作为初始下标a继续调用查找函数查找数组L的后半段
else
return(Search(L,a,m-1,x));
//如果需要查找的值≤中间值,则将下标为m-1的作为结束下标b继续调用查找函数查找数组L的前半段
}
}
折半查找
对于有序数组{3,14,27,39,42,55,70,85,93,98}进行折半查找。
找到每个元素所需的比较次数=该元素到根节点的边数+1.
成功找到数组中10个元素的平均比较次数
(1+2+3+3+4+2+3+3+4+4)/10=29/10.
失败查找所需的平均比较次数
(3+3+3+4+4+3+4+4+3+4+4)/11=39/11
回溯法
回溯法是一种深度优先的搜索法,一条分支走到底,无法再深入了然后回溯一步再深入。
回溯法可以走完一个问题的所有解,也可以达到结果之后不继续进行。如果一个问题的求解过程中需要进行逐步试探,例如迷宫问题,一般可以考虑回溯法
贪心法
顾名思义贪心法就是每次都贪图最好的东西,一般可以快速得到满意的解,但是最终的结果不一定是最优解。
例
经典背包问题,如图所示,背包容量只有70,物品1、2、3为整体不可拆分且每个物品只有一个,如果使用贪心法进行选择,那么每次都会选择单价最高的,首选物品1,再选物品2是一个比较满意的解,但不是最优解,最优解是先选物品2,再选物品3。
此类问题需注意题目要求,物品只有一个还是多个,物品是否可以拆分等。如果有多个物品则最优解为:
判断一个问题是不是贪心法的主要条件是查看是否每一步选择的都是最优但是结果不一定是最优。
动态规划法
动态规划法与分治法类似,也是将大问题拆分成小问题,解决小问题得出结果最后再合并得到大问题结果,但是动态规划法拆分成的小问题并不完全独立,可能存在关联,所以需要一张表把问题与结果对应记录下来,因此动态规划法的显著特征是具有查表操作。