1、回溯搜索法能解决的问题
(1)组合问题:例如1,2,3,4中有几种长度为2的组合方式12,13,14,23,24,34。不需要顺序
(2)切割问题:讲一个字符串切割后,保证其切割的子串都是回文子串。
(3)子集问题:例如1,2,3,4中所有的子集。不需要顺序
(4)排列问题:例如1,2,3,4中几种长度为2的组合方式12,13,14,23,24,34,31,21,41,32,42,43。需要顺序。
(4)棋盘问题:n皇后问题
使用循环很难解决上述的问题,因此以上问题必须使用回溯法来解决。
2、回溯搜索法的思路
回溯法可以抽象为一个树形结构,回溯可以理解为一棵树的横向宽度的for循环和纵向深度的递归。
3、写代码的一般套路(模板)
public void backtracking()//定义回溯函数
{
//1、终止条件
if(终止条件)
{
收集结果;
return ;
}
//2、for循环执行处理节点,递归函数,回溯函数
for(遍历集合元素)
{
处理节点
递归函数
回溯函数
}
return 最终的遍历结果。
}
4、回溯算法的应用-组合问题
首先提出问题,取出1,2,3,4中所有的长度为2的集合
代码如下:
for(int i=0;i<num.length;i++)
{
for (int j=i;j<num.length;j++)
{
System.out.print(i);
System.out.print(j);
}
}
但是如果我们将上面的数组换为100个数的长度为50的组合就需要50层的for循环进行嵌套,这显然是不现实的。
这个时候我们就需要进行回溯算法
解释一下这个图,为什么每取一个值的时候,后面的分枝就没有这个数了呢,因为,组合的定义是组合是没有顺序的,因此如果不讲前边的数组丢点必然会造成数据的重复。
代码实现
递归函数参数返回值
确定终止条件
单层递归函数
源代码:
List<List<Integer>> result = new ArrayList<List<Integer>>();
List<Integer> path= new ArrayList<Integer>();
List<Integer> path2;
public List<List<Integer>> combine(int n, int k)
{
backstracking(n,k,1);
return result;
}
public void backstracking(int n,int k,int startindex)
{
if(path.size()==k)
{
path2=new ArrayList<Integer>();
for (int i: path)
{
path2.add(i);
}
result.add(path2);
return ;
}
for(int i=startindex;i<=n;i++)
{
path.add(i);
backstracking(n,k,i+1);//这里为什么是i+1呢因为不能出现两个字母的重复,如果是i的话就会出现11,22,33这种情况。
path.remove(path.size()-1);
}
}
5、回溯算法的应用-组合问题的剪枝问题
如下图:如果当n=4,k=4的时候再进行纯暴力的搜索,会极大的浪费计算机的资源,因此需要对这个算法进行优化,使得这个算法的时间复杂度变低。
要改的地方主要是size改为n-(k-path.size)+1;
6、回溯算法的应用-组合总和
场景:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
思路:
代码编写思路
递归函数参数返回值
确定终止条件
单层递归函数
源代码:
import java.util.*;
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target)
{
int sum=0;
int startindex=0;
backtracking(candidates,target,sum,startindex);
return result;
}
List<List<Integer>> result=new ArrayList<List<Integer>>();
List<Integer> path =new ArrayList<Integer>();
List<Integer> path2;
public void backtracking(int[] candidates, int target,int sum,int startindex)
{
//首先确定函数的参数以及返回值
if(sum>target)
{
return ;
}
//确定终止条件
if(sum==target)
{
path2=new ArrayList<Integer>();
for(int i: path)
{
path2.add(i);
}
result.add(path2);
return;
}
//确定单层递归函数
for(int i=startindex;i<candidates.length;i++)
{
path.add(candidates[i]);
sum=sum+candidates[i];
backtracking(candidates,target,sum,i);
sum=sum-candidates[i];
path.remove(path.size()-1);
}
return ;
}
}