回溯法
1.概念和思想
回溯法:全称为探索与回溯法
- 概念:一种选优搜索法,又称试探法,按照选优条件进行向前搜索来达到目的。但是当探索到某一步时,发现现在进行的条件并不是最优解或者无法达到预期,就退回上一步重新选择,这种走不通就退回再走的技术就是回溯法
- 注:满足回溯条件的某个状态的某个点称为回溯点
- 回溯法有“通用的解题”的美称,可以系统性的搜索一个问题的所有解或任一解,采用的是深度优先策略
- 回溯法的结构就像是一个多叉树,是部分解的状态集合,每一个分叉的结点都是这个问题可能的部分解,它的儿子就是在这些分叉的基础上生成的部分解,树的根就是问题的初始状态,所以这中集合状态也叫状态空间树
。
2.解题步骤与方法
解题步骤
确定了问题的解空间结构后,回溯法将从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。开始结点成为活结点,同时也成为扩展结点。在当前的扩展结点处,向纵深方向搜索并移至一个新结点,这个新结点就成为一个新的活结点,并成为当前的扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前的扩展结点就成为死结点。此时应往回移动(回溯)至最近的一个活结点处,并使其成为当前的扩展结点。回溯法以上述工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已无活结点时为止。
运用回溯法解题的关键要素有以下三点:
(1) 针对给定的问题,定义问题的解空间;
(2) 确定易于搜索的解空间结构;
(3) 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索
如之前的迷宫的迷宫问题,可以利用这三个步骤来进行解决
解题方法
- 通常采用的解题方法有两种:递归法和递推法
递归法:
递归法就如同两面对着的镜子,在镜子里看到的会是一个嵌套一个的镜子,在程序中也不例外
思路简单,设计容易,浪费时间与空间导致效率很低,在笔试过程中可能无法通过编译
但是递归也是我们常用到的解题方式,我们熟知的斐波那契数列,就经常用递归来实现
//python方式:
def fib_recur(n):
assert n >= 0, "n > 0"
if n <= 1:
return n
return fib_recur(n-1) + fib_recur(n-2)
//C++方式:
void fib_recur(int n){
if (n <= 1)
return n;
else
return fun(n-1) + fun(n-2);
}
递推法:
算法设计相对复杂,但效率高
例如:用递推法实现斐波那契数列
//Python:
def fib_loop(n):
a, b = 0, 1 //a=0,b=1
for i in range(n+1):
a, b = b, a + b //a=b ,b=a+b
return a
//C++方式:
int fib_recur(int n){
int f0 = 1, f1 = 1;
int f2 = 0;
for(int i = 3; i <= n; i++){
f2 = f0 + f1;
f0 = f1;
f1 = f2;
}
return f2;
}
常见问题
- 装载问题
- 0-1背包问题
- 旅行售货员问题
- 八皇后问题
- 迷宫问题:代码见上一章博客
- 图的m着色问题
如背包问题和装载问题,通常会先选择贪心算法,先选择符合要求的做为状态集合,然后进行深度优先搜索
如旅行售货员问题,一个售货员把几个城市旅行一遍,要求走的路程最小。它的解就是几个城市的排列,解空间就是排列树。
有关回溯法常见问题代码:
参考链接:http://blog.csdn.net/jarvischu