本篇博客主要讲分治法、动态规划法、贪心法、回溯法、分支限界法几种常用算法的原理,方便同学们理解每种算法的道理到底是怎么回事。
分治法:
将一个难以直接解决的大问题分解为一些规模较小的相同问题各个击破,分而治之。一般分为三个步骤:分解,求解,合并。和递归相似。快速排序是一种典型的分治算法,将一个大的序列初步排序分解为两个序列,再对两个序列再进行排序分解……举个生活中的例子:比如即将进行的软考就要开始考试了,考完之后我们每个同学都会有一个成绩,之后我们就要对成绩进行排名,首先以60分及格分为基准将所有的成绩分为两部分,再对这两部分分别进行排名,小于60分的同学排一下参见下一次考试,大于60分的选择全国第50名的分数作为基准再进行分组,一部分是我们班进入全国前50名的,一部分是我们班及格了但是没有进入全国前50名的——这就是分治法
具体代码如下:
QUICKSORT(A,p,r){ //排序的主要函数,A为要排序的数组,p数组的开始位置,r为数组的结束位置
if(p<r){
q=PARTITON(A,p,r); //选出数组的一个轴
QUICKSORT(A,p,q-1);
QUICKSORT(A,q+1,r);
}
}
PARTITON(A,p,r){ //数组的一个轴的计算函数
x=A[r];
i=p-1;
for(intj=p;j<=r-1;j++){
if(A[j]<=x){
i=i+1;
交换A[i]和A[j]
}
}
交换A[i]和A[r]
returni+1;
}
动态规划法:
动态规划法也是将待求解的问题分为若干个子问题,先求解子问题的解,最后得到原来问题的解。与分治法不同的是,动态规划法分解的子问题之间往往不是独立的。分治法分解的子问题相互独立,一个子问题可能会被求解多次,导致浪费的时间比较多,而动态规划法就避免了这样的问题,它的动态就体现到了这里。只要是被计算过的子问题都要记录起来,因为将来可能会再次使用。就拿背包问题作为例子来说吧,一个小偷入室盗窃,背包的大小M是一定的,采用动态规划法就是将背包的大小从1到M逐一的考虑,每种情况可以放什么物品都记录起来,当计算背包大小为i的情况的时候,结合背包大小为i-1的情况(动态)选择合适的物品放到背包中或者维持原来的不变
具体代码如下:
MaxNutrientValue(n,v,p,M,x){
for i=0 to n
nv[i][0]=0
forj=1 to M
nv[0][j]=0
for i=1 to n
forj=1 to M
ifj<p[i]
nv[i][j]=nv[i-1][j]
elseif nv[i-1][j]>=nv[i-1][j-p[i]]+v[i]
nv[i][j]=nv[i-1][j]
else
nv[i][j]=nv[i-1][j-p[i]]+v[i]
j=M
fori=n downto 1
if nv[i][j]=nv[i-1][j]
x[i]=0
else
x[i]=1
j=j-p[i]
return x and nv[n][M]
}
贪心算法:
贪心算法就是仅根据当前的信息作出选择,现在作出选择对将来不管产生什么影响现在的选择都不会变化。就是只考虑局部,不考虑整体。典型的例子有“买东西找零钱”,这里就不细说了。
回溯法:
回溯法就是从根节点出发,以深度优先的方式搜索解空间,遇到不符合条件的就进行回溯,移动到最近的一个活结点,直到没有活结点为止。典型的例子有“八皇后”问题
分支限界法:
分支限界法就与回溯法相对应,回溯法是求解目标中满足约束条件的所有解空间,分支限界法则是找满足约束条件的一个解(最优解)