目录
回溯法定义:
回溯法是对穷举法(也叫枚举法,蛮力法)的优化,即穷举过程中一旦发现某条路径不可能满足要求时就回溯(即不再沿这条路进行前进,回退到上一步),故效率一般比穷举法高。对深度优先搜索可用回溯法进行剪枝从而提高搜索效率。
回溯法的具体应用案例:
1、求解n皇后问题:
完整代码展示:
package leetcode_7;
public class T_52 {
public static void main(String[] args) {
System.out.println(totalNQueens(4));
}
public static int totalNQueens(int n) {
int[] count=new int[1];
int[] q=new int[n];
f(0,n,count,q);
return count[0];
}
private static void f(int i,int n,int[] count,int[] q) {
if(i>=n) {
count[0]++;
return ;
}
for(int j=0;j<n;j++) {
if(place(i,j,q)) {//如果条件成立,说明此路暂时可行,进行递归找下一个皇后位置
q[i]=j; //如果条件不成立,则直接跳过此路
f(i+1,n,count,q);
}
}
}
private static boolean place(int i,int j,int[] q) {
if(i==0)
return true;
int k=0;
while(k<i) {
if((j==q[k])||(Math.abs(i-k)==Math.abs(j-q[k])))
return false;
k++;
}
return true;
}
}
2、求解组合总和问题:
完整代码展示:
package leetcode_7;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class T_39 {
public static void main(String[] args) {
int[] a= {2,3,6,7};
System.out.println(combinationSum(a,7));
}
public static List<List<Integer>> combinationSum(int[] candidates, int target){
List<List<Integer>> ans=new LinkedList();
Arrays.sort(candidates);
for(int i=0;i<candidates.length;i++) {
if(candidates[i]<=target) {
List<Integer> p=new LinkedList();
p.add(candidates[i]);
f(candidates,target-candidates[i],i,ans,p);
}
}
return ans;
}
private static void f(int[] a,int t,int i,List<List<Integer>> ans,List<Integer> p) {
if(t==0) {
ans.add(p);//已到达终点,不再此路径上继续走下去
return ;
}
if(t<0||t-a[i]<0)
return ; //已不可能到达终点,退出此路径
for(;i<a.length;i++) {
if(t-a[i]>=0) {
List<Integer> q=new LinkedList(p);
q.add(a[i]);
f(a,t-a[i],i,ans,q);
}
else //后面情况不可能满足,剪枝
break;
}
}
}
完整代码展示:
package leetcode_7;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class T_40 {
public static void main(String[] args) {
int[] a= {10,1,2,7,6,1,5};
System.out.println(combinationSum2(a,8));
}
public static List<List<Integer>> combinationSum2(int[] candidates, int target){
List<List<Integer>> ans=new LinkedList();
Arrays.sort(candidates);
for(int i=0;i<candidates.length;i++) {
if(candidates[i]<=target) {
if(i>0&&candidates[i]==candidates[i-1])//从发源地去重,可直接避免不必要的搜索
continue;
List<Integer> p=new LinkedList();
p.add(candidates[i]);
f(i,target-candidates[i],ans,p,candidates);
}
}
return ans;
}
private static void f(int start,int t,List<List<Integer>> ans,List<Integer> p,int[] a) {
if(t==0) {
ans.add(p);//满足条件,保存答案,退出此路
return ;
}
if(start>=a.length-1||t<0||t-a[start+1]<0)
return ;//对于已经走完或已不可能满足要求的路径,退出
for(int i=start+1;i<a.length;i++) {
if(t-a[i]>=0) {
if(i>start+1&&a[i]==a[i-1])
continue ;//去重
List<Integer> q=new LinkedList(p);
q.add(a[i]);
f(i,t-a[i],ans,q,a);
}
else//对于已不可能满足要求的路径直接退出
break;
}
}
}
完整代码展示:
package leetcode_7;
import java.util.LinkedList;
import java.util.List;
public class T_216 {
static List<List<Integer>> ans=new LinkedList();
static LinkedList<Integer> p=new LinkedList();
public static void main(String[] args) {
System.out.println(combinationSum3(3,9));
System.out.println(combinationSum3(4,1));
}
public static List<List<Integer>> combinationSum3(int k, int n){
ans=new LinkedList();//由于将ans定义为静态变量,所有值共享,
int sum=0; //故每次调用前应该把ans清0,避免多次调用此方法时后面答案受前面答案影响
for(int i=1;i<=9;i++)
sum+=i;
f(k,1,n,sum);
return ans;
}
private static void f(int k,int start,int n,int sum) {
if(k==0) {//k为每个组合还需要数字的个数,k为0时,无论是否满足要求,都退出
if(n==0)//若满足要求,则添加组合
ans.add(new LinkedList(p));
return ;
}
for(int i=start;i<=9;i++) {
if(n-i>=0&&n-i-sum<=0) {
p.add(i);
sum-=i;
f(k-1,i+1,n-i,sum);
p.removeLast();//将刚添加的值删除
}
else//当已不可能满足要求时,直接退出
break;
}
}
}
3、求解全排列问题:
package leetcode_7;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class T_47 {
static List<List<Integer>> ans;
static boolean[] map;
static List<Integer> path;
public static void main(String[] args) {
int[] a= {1,1,2};
System.out.println(permuteUnique(a));
}
public static List<List<Integer>> permuteUnique(int[] nums){
Arrays.sort(nums);
ans=new LinkedList();
path=new LinkedList();
map=new boolean[nums.length];
f(nums);
return ans;
}
private static void f(int[] nums) {
if(path.size()==nums.length) {
ans.add(new LinkedList(path));
return ;
}
int p=-11;
for(int i=0;i<nums.length;i++) {
if(map[i])
continue;//保证已经在path中的元素不会再被取到
if(nums[i]==p)
continue;//保证本次遍历的元素值不重复
map[i]=true;
p=nums[i];
path.add(nums[i]);
f(nums);
path.remove(path.size()-1);
map[i]=false;
}
}
}
总结:
总之,回溯法就是提前筛选出不满足要求的情况,一般用于DFS中,称为剪枝。相比于先搜索所有答案再从中挑选符合要求的答案,回溯法是在搜索过程中就将不符要求以及重复答案剔除,大大提高了效率。
本文介绍了回溯法的基本概念及其在解决经典问题中的应用,包括N皇后问题、组合总和问题及全排列问题等,并提供了详细的代码实现。
629

被折叠的 条评论
为什么被折叠?



