(十九)回溯法
回溯法和DFS有区别
backtraking有一个诀窍 就是recursive call之后要退回到之前的一个状态。
基本思想
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
解题一般步骤
(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
一、排列组合问题
public static List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
ArrayList<Integer> al=new ArrayList<Integer>(); //存放路径上已经有的节点
boolean[] userd=new boolean[nums.length];
//深度遍历,返回条件是没有可选的了,则返回,所以深入每层要看是否还有可选的
DFS(nums,res,al,userd);
for(int i=0;i<res.size();i++)
{
List<Integer> tmp=res.get(i);
for(int j=0;j<tmp.size();j++)
{
System.out.print(tmp.get(j)+",");
}
System.out.println();
}
return null;
}
public static void DFS(int[] nums,List<List<Integer>> res,ArrayList<Integer> al,boolean[] userd) {
if(al.size()==nums.length)
res.add((ArrayList<Integer> )al.clone());
for(int i=0;i<nums.length;i++)
{
if(!userd[i])
{
userd[i]=true;
al.add(nums[i]);
DFS(nums,res,al,userd) ;
//回复状态
al.remove(al.size()-1);
userd[i]=false;
}
}
}
public static void main(String[] args) {
int[] nums={1,2,3};
permute(nums) ;
}
注意点:①当回溯时,要恢复状态,可以简单举个例子就可以看出来怎么做
②深拷贝:
res.add(al):浅拷贝
res.add((ArrayList<Integer> )al.clone());