回溯模版
java
void backtracking(参数) {
if (终止条件) {
存放结果; //将path存入res
return;
}
for (选择 : 本层集合中的元素) {
处理节点;
backtracking(路径, 选择列表); // 递归
撤销处理; // 回溯
}
}
java
class Solution {
List <List<格式>> res=new ArrayList<>();
public List<List<String>> 名字(String s) {
List<格式> path=new ArrayList<>();
dfs(s,path,0);
return res;
}
public void dfs(String s,List<String> path,int start){
//终止条件
if(start==s长度){
res.add(new ArrayList<>(path));
return;
}
//极端边界条件
if(xxx){xxxx;}
//递归遍历
可能的参数替换、截取、选字母
for(int i=start;i<s长度;i++){
path.add(temp);
dfs(s,path,i+1);
path.remove(path.size()-1);
}
}
}
46-全排列
**思路:**全排列回溯 选用DFS递归;结果要全部情况,返回用二维动态列表; 每次存一个结果,并做使用记录,递归条件path满了就return做下一个;path里遍历每一个数字 判断增删 ,递归下一层入口也在这里。
**注意:**存整体结果用二维动态列表,且全局;
使用记录uesd默认false;
path满了,给res复制一份,不能直接传地址,必须new ArrayList复制一份;
java
class Solution {
List <List<Integer>> res =new ArrayList<>();//存返回结果动态二维列表,全局dfs要用
public List<List<Integer>> permute(int[] nums) {
List <Integer> path =new ArrayList<>();//存单个的
boolean[] used =new boolean[nums.length]; //使用记录,默认false
dfs(nums,path,used);
return res;
}
public void dfs(int[]nums,List<Integer> path,boolean[] used){
//递归终止条件
if(path.size()==nums.length){ //当前path满了,存入一种结果
res.add(new ArrayList<>(path));//将path存入res,path动态改变 直接传的话是地址,需要重新复制一份
return;
}
//遍历每一个数字
for(int i=0;i<nums.length;i++){
if(used[i]){continue;} //用过就跳过,used初始值默认false
path.add(nums[i]);//未用过就添加
used[i]=true; //标记已使用
dfs(nums,path,used);
path.remove(path.size()-1);//移除path的最后一个,动态用size.()
used[i]=false;
}
}
}
78-子集
**思路:**回溯DFS和上一题一样;使用start参数来控制下一层起点;不需要记录used值
注意: 注意传参,i从start开始算,递归时start走下一个
java
class Solution {
List <List<Integer>> res =new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> path=new ArrayList<>();
dfs(nums,0,path); //传入参数start为0,来控制下一层起点,避免回头重复
return res;
}
public void dfs(int[] nums,int start,List<Integer> path){
res.add(new ArrayList(path)); //无需判断,直接先加上
for(int i=start;i<nums.length;i++){ //看起点从start开始
path.add(nums[i]);
dfs(nums,i+1,path); //注意参数增一,代表下一层
path.remove(path.size()-1);
}
}
}
17-电话号码字母组合
**思路:**回溯DFS和模板一样,传入是字符串的数字"23" ,所以依次取字符 并变成数字,用数字取其对应的按键字符串,开始遍历字符串,选a------选d------删d------选e------删e------选f------删f------删a,两层for循环。
注意:res用一维String列表即可;
判断特殊条件输入为空,即digits长度为0;
path类型为StringBuilder,string为拆开的'a' 'd',而输出为整体字符串'ad',且可变;
数组用 .length 字符串用 .length() 集合用size.() ;
添加: List用add( ) , StringBuilder用append.( ) ;
删除: List用remove( ) , StringBuilder用deleteCharAt.( ) ;
path的格式是StringBuildes,统一转成string用toString( );
charAt.( i ) 取字符串的第i个字母;
取完字母 - '0' 就能变成数字;
java
class Solution {
List <String> res=new ArrayList<>();
// 数字对应字母
String[] map = {
"", // 0
"", // 1
"abc", // 2
"def", // 3
"ghi", // 4
"jkl", // 5
"mno", // 6
"pqrs", // 7
"tuv", // 8
"wxyz" // 9
};
public List<String> letterCombinations(String digits) {
if(digits.length()==0){return res;} //特殊条件为空时
StringBuilder path = new StringBuilder();//为完整字符,不能拆开,string是拆开单独的
dfs(digits,0,path);
return res;
}
public void dfs(String digits,int index,StringBuilder path){
if(index==digits.length()){//终止条件,数组.length ,字符串.length()
res.add(path.toString());//将path存入res,注意两者格式不一样需统一
return;
}
//传入的是字符串,需要变成数字取出来
char digit=digits.charAt(index); //charAt()取第i个字符为digit
String letters=map[digit-'0']; // 取该位置的map的字母内容,存为letters
for(int i=0;i<letters.length();i++){ //开始遍历letters
path.append(letters.charAt(i)); //添加第i个字母
dfs(digits,index+1,path);//走下一按钮字母串,再执行for循环
path.deleteCharAt(path.length()-1);//撤回
}
}
}
39-组合总和
**思路:**和以前一样,只是递归传参包含了i本身 不需要+1 , 使用start做起始值 保证不能回头找 只能向下一步走(包括本身)
**注意:**和78-子集一样
java
class Solution {
List <List<Integer>> res=new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List <Integer> path=new ArrayList<>();
dfs(candidates,target,0,path);
return res;
}
public void dfs(int[] candidates, int target,int start, List <Integer> path){
if(target==0){ //target减为0时
res.add(new ArrayList(path));
return ;
}
if(target<0){return;} //target不匹配时 即减到底,无反应
//i从start开始,保证不重复223,322,232
for(int i=start;i<candidates.length;i++){
path.add(candidates[i]);
dfs(candidates,target-candidates[i],i,path); //传参target要减,起始位置变成i 代表本身可重复用
path.remove(path.size()-1);
}
}
}
22-括号生成
**思路:**只有左右括号,终止条件是左右之和为2n, 左的执行条件是 <n , 右的执行条件是<左 ,左右单独加1
注意:写法StringBuilder path=new StringBuilder();
字符串用**" "** ,字符用**' '** ,**(**是字符串;
path类型为StringBuilder,string为拆开的'a' 'd',而输出为整体字符串'ad',且可变;
数组用 .length 字符串用 .length() 集合用size.() ;
添加: List用add( ) , StringBuilder用append.( ) ;
删除: List用remove( ) , StringBuilder用deleteCharAt.( ) ;
path的格式是StringBuildes,统一转成string用toString( );
java
class Solution {
List <String> res=new ArrayList<>();
public List<String> generateParenthesis(int n) {
StringBuilder path=new StringBuilder();//由于是字符 则需要ListBilder
dfs(n,path,0,0);
return res;
}
//需要左右分开操作
public void dfs(int n,StringBuilder path,int left,int right){
//终止条件 当左右的和为2n时,代表可结束
if((left+right)==2*n){
res.add(path.toString());
return;
}
//若左<n,代表可继续
if(left<n){
path.append("(");//字符串用"" ,字符用'',(是字符串
dfs(n,path,left+1,right);
path.deleteCharAt(path.length()-1); //
}
//若右<左,代表可继续
if(right<left){
path.append(")");
dfs(n,path,left,right+1);
path.deleteCharAt(path.length()-1); }
}
}
79-单词搜索
**思路:**要找单词 所以要遍历所有点,在每个点进行DFS, 判断当前字符是否=word [index] ,用完标记,新建found遍历 下一层,然后回溯
注意: 字符串用length.() , 字符用 length;
边界写>= ,必须有=;
字符串里选字母 用charAt ;
用完必须标记
java
class Solution {
public boolean exist(char[][] board, String word) {
for(int i=0;i<board.length;i++){//遍历所有的点,都进行遍历
for(int j=0;j<board[0].length;j++){
if(dfs(board,i,j,word,0)){
return true;
}
}
}
return false;
}
public boolean dfs(char[][]board,int i,int j,String word,int index){
if(index==word.length()){return true;}//递归终止条件
if(i<0||i>=board.length||j<0||j>=board[0].length){return false;}//超出边界条件
if(board[i][j]!=word.charAt(index)){return false;}//不匹配条件,取字符串某字母用charAt
char temp=board[i][j];//暂存字符 方便回溯
board[i][j]='#';//用完标记
boolean found= dfs(board,i-1,j,word,index+1)//迭代
||dfs(board,i+1,j,word,index+1)
||dfs(board,i,j-1,word,index+1)
||dfs(board,i,j+1,word,index+1);
board[i][j]=temp;//回溯
return found;//返回
}
}
131-分割回文数
**思路:**这题不需要理解,死记硬背就行,模版和其他的一样,内部裁剪切割有点复杂
**注意:**传的参数
java
class Solution {
List <List<String>> res=new ArrayList<>();
public List<List<String>> partition(String s) {
List<String> path=new ArrayList<>();
dfs(s,path,0);
return res;
}
public void dfs(String s,List<String> path,int start){
//终止条件
if(start==s.length()){
res.add(new ArrayList<>(path));
return;
}
for(int i=start;i<s.length();i++){
String temp=s.substring(start,i+1);//截取子串判断
if(isHui(temp)){
path.add(temp);
dfs(s,path,i+1);
path.remove(path.size()-1);
}
}
}
//判断是否是回文数
public boolean isHui(String temp){
int left=0;
int right=temp.length()-1;
while(left<right){
if(temp.charAt(left)!=temp.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
}











