回溯算法
回溯法实际是一种避免不必要搜索的穷举式搜索法。
用回溯法解题一般步骤:
- 确定问题解空间,判断是使用子集树还是使用排列树
- 以深度优先的方式搜索解空间树,在搜索过程中要判断要是用剪枝函数避免无效搜索。
回溯算法可分为递归回溯和迭代回溯
这篇文章使用递归回溯来进行解决,递归回溯更加简单直观,便于理解。
递归回溯算法步骤:
子集树实例:解决 0 1 背包问题
0 1 背包问题适合使用子集树来进行解决,对子集树的描述如下:
Package.h
1 |
|
Package.cpp
1 |
|
mian.cpp
1 |
|
仔细看看可以看到,0 1 背包的实际代码与上图中代码逻辑是一致的。(有时候,对算法的总结抽象很重要)
之前甚至目前我学习算法,觉得算法描述与实际代码之间相隔很远,就算给我算法描述我可能也无法写出代码。实际上,算法描述与代码之间确实有一定差距,而这差距就需要我们自己进行编程解决,比如 上图描述子集树的算法中output函数,算法描述很简单,但是需要我们按照我们的实际要求进行填充。
剪枝优化:
上面的的代码中其实已经进行了一步剪枝操作(所谓剪枝,就是提前将不符合条件的去掉),在
1 | if (currentWeight + t * w[i] <= content) |
处判断是否符合背包容量要求来进行剪枝避免无效搜索。
除此之外,当目前已经放入背包中的物品的价值 + 还没有考虑的物品总价值 仍然小于 目前得到的最大价值bestPrice时, 就没有继续搜索下去的必要了,就可以进行剪枝。
添加剪枝函数bound后的代码:
Package.h
1 |
|
Package.cpp
1 |
|
子集树实例:n皇后问题
直接看代码:
NQueen.h
1 |
|
NQueen.cpp
1 |
|
代码注释解释的很详细了。这里要说明的是这里和普通的子集树又不一样,平常的子集树的一维的,但是这里等于是二维的。但是回溯的思想是一样的。
子集树实例:最大团问题
一个无向图 G=(V,E),V 是点集,E 是边集。取 V 的一个子集 U,若对于 U 中任意两个点 u 和 v,有边 (u,v)∈E,那么称 U 是 G 的一个完全子图。 U 是一个团当且仅当 U 不被包含在一个更大的完全子图中。
G的最大团指的是定点数最多的一个团。
直接看代码就可以:
MaxClique.h
1 |
|
MaxClique.cpp
1 |
|
Main.cpp
1 |
|
从上面的代码中可以看到,还以使用子集树一样的套路,不过这里要注意的是需要根据不同的题来进行不同的具体使用。
例如这个题,再像处理0 1 背包那样(如下图的操作)
就不行了,这里要通过if else 来代替for 循环,这样可以更好的判断向已有的团中加入一个节点是否满足团的条件。
根据具体情况进行具体分析的能力和思想很重要。算法只能提供给我们一种思想,但是我们不能把这种思想僵化为一种固定模式,算法是动态随机应变的。
排列树实例:全排列问题
比如:集合{ 1,2,3}的全排列为:
{ 1 2 3}
{ 1 3 2 }
{ 2 1 3 }
{ 2 3 1 }
{ 3 2 1 }
{ 3 1 2 }
通过全排列问题可以找到排列树的实质原理,因为排列树就是对一个序列进行全排列。
直接上代码:
1 |
|
排列树实例:解决TSP问题:
TSP问题解决可以使用排列树,排列树的算法描述为:
TSP.h
1 |
|
TSP.cpp
1 | /* |
main.cpp
1 |
|
上面代码是我自己写的,因为我把排列树的基本忘了,我就按着自己的理解写了。虽然最后结果是正确的,但是很显然代码质量很差,我在编码的过程中感觉自己想的有些复杂,但是又不能写出更好的。硬着头皮写完后参考了网上的资料。其实按着求全排列那样写就可以。
回溯算法其他问题:
子集树解决装载问题 :
leetcode Letter Combinations of a Phone Number
https://leetcode.com/problems/letter-combinations-of-a-phone-number/
经过这个题可以发现,对于回溯问题,并不是所有的要使用子集树或排列树,比如当寻找[a,b,c] 和 [d,e,f]的搭配时(ad,ae..) ,子集树和排列树好像都不那么合适,这时候就需要具体问题具体分析了:
回溯法时间复杂度
一般用回溯法都是一些NP问题。
回溯法的最坏复杂度就是无解的时候。因为想证明无解就得否定所有的解,也就是说会考虑所有的可能成为解的情况。
例如用排列树皇后问题,复杂度是O(n!),因为等价于找到1~n的其中一个排列。
又例如01背包,当然在容量以及物品的体积在一个比较小的范围可以动态规划。然而在一般情形,只能搜索2^n种情况。所以就是O(2^n).
基本上回溯法就是两种复杂度,指数或者阶乘。