算法设计经典算法

一、贪婪算法

1、概述

贪婪法又称贪心算法,是当追求的目标是一个问题的最优解时,设法把对整个问题的求解工作分成若干步骤来完成,是寻找最优解问题的常用方法。

贪婪法的特点是一般可以快速得到满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪婪法常以当前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪婪法不要回溯。如装箱问题和和着色问题都可以采用贪婪法来求解。

2、问题实例

现在根据实际问题来说明贪婪法的算法思想。

装箱问题

问题描述:装箱问题可简述如下:设有编号为0、1、…、n-1的n种物品,体积分别为v0、v1、…、vn-1。将这n种物品装到容量都为V的若干箱子里。约定这n种物品的体积均不超过V,即对于0≤i<n,有0<vi≤V。不同的装箱方案所需要的箱子数目可能不同。装箱问题要求使装尽这n种物品的箱子数要少。 
   若考察将n种物品的集合分划成n个或小于n个物品的所有子集,最优解就可以找到。但所有可能划分的总数太大。对适当大的n,找出所有可能的划分要花费的时间是无法承受的。为此,对装箱问题采用非常简单的近似算法,即贪婪法。该算法依次将物品放到它第一个能放进去的箱子中,该算法虽不能保证找到最优解,但还是能找到非常好的解。不失一般性,设n件物品的体积是按从大到小排好序的,即有v0≥v1≥…≥vn-1。如不满足上述要求,只要先对这n件物品按它们的体积从大到小排序,然后按排序结果对物品重新编号即可。装箱算法简单描述如下: 
{   输入箱子的容积; 
   输入物品种数n; 
   按体积从大到小顺序,输入各物品的体积; 
   预置已用箱子链为空; 
   预置已用箱子计数器box_count为0; 
   for (i=0;i<n;i++) 
   {   从已用的第一只箱子开始顺序寻找能放入物品i 的箱子j; 
      if (已用箱子都不能再放物品i) 
      {   另用一个箱子,并将物品i放入该箱子; 
         box_count++; 
      } 
      else 
         将物品i放入箱子j; 
   } 

   上述算法能求出需要的箱子数box_count,并能求出各箱子所装物品。下面的例子说明该算法不一定能找到最优解,设有6种物品,它们的体积分别为:60、45、35、20、20和20单位体积,箱子的容积为100个单位体积。按上述算法计算,需三只箱子,各箱子所装物品分别为:第一只箱子装物品1、3;第二只箱子装物品2、4、5;第三只箱子装物品6。而最优解为两只箱子,分别装物品1、4、5和2、3、6。 
 二、分治法

1、概述

分治法的基本思想是把一个规模较大的问题分成两个或者多个较小的和原问题相似的子问题,首先对子问题进行求解,然后再把各个子问题的解合并起来,得出整个问题的解。

2、问题实例

分治法的三个步骤:

step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。

实例:

在一个未排序的数组中找到第k大元素,即排序后的第k大的数。

分析:

总是将要划界的数组段末尾的元素为划界元,将比其小的数交换至前,比其大的数交换至后,最后将划界元放在“中间位置”(左边小,右边大)。划界将数组分解成两个子数组(可能为空)。

设数组下表从low开始,至high结束。
1、 总是取要划界的数组末尾元素为划界元x,开始划界:

a) 用j从low遍历到high-1(最后一个暂不处理),i=low-1,如果nums[j]比x小就将nums[++i]与nums[j]交换位置

b) 遍历完后再次将nums[i+1]与nums[high]交换位置(处理最后一个元素);

c) 返回划界元的位置i+1,下文称其为midpos

这时的midpos位置的元素,此时就是整个数组中第N-midpos大的元素,我们所要做的就像二分法一样找到K=N-midpos的“中间位置”,即midpos=N-K

2、 如果midpos==n-k,那么返回该值,这就是第k大的数。
3、 如果midpos>n-k,那么第k大的数在左半数组
4、 如果midpos<n-k,那么第k大的数在右半数组

三、回溯法

1、概念

回溯法也称试探法,该方法首先暂时放弃关于问题规模大小的限制,并将问题的候选解按照某种顺序之一枚举和检验。回溯法的基本思想是采用深度优先策略,一步一步向前试探的方法,当某一步有多种选择时,可以先任意选择一种,继续向前试探。一旦发现到达某步后无法再前进,说明上一步做的选择有问题,就后退到上一步重新选择(这就称为回溯)。回溯法的特点是可以避免穷举式的盲目搜索,从而可能减少问题求解的搜索时间。如迷宫问题和八皇后问题都可以采用回溯方法来设计求解算法。
2、问题实例

回溯法的一般步骤:

(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。

(2)确定结点的扩展搜索规则

(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

八皇后问题
问题描述:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

《算法设计经典算法》

转化规则:其实八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当 n = 1 或 n ≥ 4 时问题有解。令一个一维数组a[n]保存所得解,其中a[i] 表示把第i个皇后放在第i行的列数(注意i的值都是从0开始计算的),下面就八皇后问题的约束条件。
(1)因为所有的皇后都不能放在同一列,因此任意两个a[0]…..a[7]的值不能存在相同的两个值。
(2)所有的皇后都不能在对角线上,那么该如何检测两个皇后是否在同一个对角线上?我们将棋盘的方格成一个二维数组,如下:

《算法设计经典算法》

假设有两个皇后被放置在(i,j)和(k,l)的位置上,明显,当且仅当|i-k|=|j-l| 时,两个皇后才在同一条对角线上。

四、动态规划法

1、概述

动态规划与分治法相似,都是把一个大问题分解为若干较小的子问题,通过求解子问题而得到原问题的解.不同的是分治法每次分解的子问题数目较少,子问题之间界限清楚,处理的过程通常是自顶向下进行;而动态规划法分解子问题可能较多,而且子问题相互包含,为了重用已经计算的结果,要把计算的中间结果保存起来,动态规划法通常是自底向上进行。

 2、问题实例

动态规划的思路是通过寻找最优子结构同时记录最优结构,从而将复杂的大问题转化为小问题的求解过程。解决动态规划类问题,分为两步:1.确定状态,2.根据状态列状态转移方程

例题:揹包问题1

在n个物品中挑选若干物品装入揹包,最多能装多满?假设揹包的大小为m,每个物品的大小为A[i]。

首先寻找状态,确定将什么作为状态,记录状态,有揹包和物品,物品有放和不放两种状态,放置的时候可能会对应各种容量,当前的容量下可以放置进的最多的物品取决于上一个物品放置时在该状态下所能够达到的最大状态和当前物品的的大小,这样我们在最后,就可以得到每种容量下,所能放置的物品的最大数量。

揹包问题2:

给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的揹包,最多能装入的总价值有多大?

考虑到价值问题,状态不发生变化,只是对于状态我们所记录的内容方式变化,我们现在记录的是其价值,而不是其放置的物品的大小。

 五、分支限界法

1、概述

分枝界限法(Branch and Bound)与回溯法相似,也是一种在全部问题解的空间中进行系统搜索的方法。所不同的是,回溯法使用深度优先策略,而分支界限法可以采用广度优先策略。与此同时,分支界限法在搜索过程中,还利用最优解属性的上下界来控制搜索的分支,剪去不必再花时间搜索的部分,从而提高搜索的效率。
2、算法

算法基本思想如下:

1)以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树
2)分支限界法中,每一个活结点只有一次机会成为扩展结点,活结点一旦成为扩展结点,就一次性产生其所有儿子结点,其中导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中
3)然后从活结点表中取下一结点成为当前扩展结点
4)重复上述结点扩展过程,直至到找到所需的解或活结点表为空时为止

分支限界法与回溯法的区别:

1)求解目标不同 
        回溯法的求解目标是找出解空间树中满足约束条件的所有解
        分支限界法的求解目标则是尽快找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解
        分支限界法通常用于解决离散值的最优化问题
2)搜索方式不同 
        回溯法以深度优先的方式(遍历结点)搜索解空间树
        分支限界法以广度优先或最小耗费优先的方式搜索解空间树
3)对扩展结点的扩展方式不同 
        分支限界法中,每一个活结点只有一次机会成为扩展结点
        活结点一旦成为扩展结点,就一次性产生其所有儿子结点
4)存储空间的要求不同 
        分支限界法的存储空间比回溯法大得多,因此当内存容量有限时,回溯法成功的可能性更大

参考:

https://blog.csdn.net/K346K346/article/details/50774803

https://blog.csdn.net/jia_xiaoxin/article/details/2750880

https://blog.csdn.net/EbowTang/article/details/51218500

https://segmentfault.com/a/1190000004498566

https://blog.csdn.net/u010089444/article/details/74331907

点赞