1、回溯法
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
- 如果需要求所有的解,则需要对每个状态进行搜索。(函数返回值设为void)
- 如果要求存在一个解,那么找到某个解直接返回即可。(函数返回值设为boolean)
2、算法框架
- 结果收集条件(回溯结束条件)
- 继续进入下一层搜索条件
- 搜索上界下界
//求所有解的算法框架
public void backtracting(temp){
if("temp是一个结果"){ //结果收集条件
加入结果集;
return;
}
for(j=start;j<= end;j++){
if("不满足加入条件") continue;
temp.add(a); //加入当前元素
backtracting(j+1); //继续进行下一步搜索
temp.remove(a); //回溯的清理工作,把上一步的加入结果删除
}
}
//求存在解的算法框架
public boolean backtracting(temp){
if("temp是一个结果"){ //结果收集条件
加入结果集;
return true;
}
for(j=start;j<= end;j++){
if("不满足加入条件") continue;
temp.add(a); //加入当前元素
if(backtracting(j+1))
return true; //继续进行下一步搜索
temp.remove(a); //回溯的清理工作,把上一步的加入结果删除
return false;
}
}
3、回溯法解题的一般步骤
(1)针对所给问题,确定问题的解空间: 首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则,搜索的结果一定要包括要求的解。
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索(求存在解的问题会出现剪枝)。
4、常见题目示例
77. Combinations
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
题目:从1----n中选出k个数作为组合,求出所有的组合具体的序列。
思路:回溯法,每次从下界开始遍历,到上界结束。满足size() == k 则加入结果,所有解都求出则结束遍历。也可以用二进制模拟的方法。
public class Solution {
List<List<Integer>> res = new LinkedList<List<Integer>>();
public List<List<Integer>> combine(int n, int k) {
if(k==0)
return res;
helper(k,n,1,new LinkedList<Integer>());
return res;
}
public void helper(int k,int n,int start,List<Integer> out){
if(k<=0)
return;
if(k == out.size()){
List<Integer> temp = new LinkedList<Integer>(out);
res.add(temp);
return;
}
for(int i=start;i<=n;i++){
out.add(i);
helper(k,n,i+1,out); //因为是组合,元素无关顺序,所以每次从下一步进行搜索
out.remove(out.size()-1);
}
}
}
39. Combination Sum
Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number