1.组合总和问题
思路:回溯模板
(1)确定方法返回值和参数
分析可知遍历数组然后求和值,不需要返回什么值
参数也就是candidates,list,path,target,start
(2)确定回溯终止条件
当满足求和的时候终结
(3)确定单层逻辑
判断当前是否满足条件是就添加进条件,然后返回
不是就继续向下递归,这里需要注意从i开始,也就是可以取自己
当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的值
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
trace(list,path,candidates,target,0);
return list;
}
public void trace(List list,List path,int[] candidates,int target,int start){
if(target<0){
return;
}
if(target == 0){
list.add(new ArrayList<>(path));
return;
}
for(int i = start;i<candidates.length;i++){
path.add(candidates[i]);
trace(list,path,candidates,target - candidates[i],i);
path.remove(path.size()-1);
}
}
}
2.分割回文串
思路:
(1)确定方法返回值和参数
分析可知遍历字符串进行分割,不需要返回什么值
参数也就是s,list,path,start
(2)确定回溯终止条件
当遍历完字符串就结束一次递归
(3)确定单层逻辑
判断当前分割方案是否是回文串不是继续往后纳入字母
是就继续向下递归,这里需要注意从i+1开始,也就是不可以取自己
当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的方案
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> list = new ArrayList<List<String>>();
List<String> path = new ArrayList<String>();
trace(s,0,list,path);
return list;
}
public boolean isPalindrom(String s,int l,int r){
for(int i = l,j = r;i<j;i++,j--){
if(s.charAt(i) != s.charAt(j)){
return false;
}
}
return true;
}
public void trace(String c,int start,List list,List path){
if(start >= c.length()){
list.add(new ArrayList<String>(path));
return;
}
for(int i = start;i<c.length();i++){
if(!isPalindrom(c,start,i)){
continue;
}
String str = c.substring(start,i + 1);
path.add(str);
trace(c,i+1,list,path);
path.remove(path.size()-1);
}
}
}
3.子集问题
思路:子集问题不需要剪枝,遍历全部并且每次都加添加进结果集
(1)确定方法返回值和参数
分析可知遍历数组,不需要返回什么值
参数也就是nums,list,path,start
(2)确定回溯终止条件
当遍历完数组就结束一次递归
(3)确定单层逻辑
将当前数字添加进path,继续向下递归,这里需要注意从i+1开始,也就是不可以取自己
当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的数字
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
trace(list,path,nums,0);
return list;
}
public void trace(List list,List path,int[] nums,int start){
if(start > nums.length){
return;
}
list.add(new ArrayList<>(path));
for(int i = start;i<nums.length;i++){
path.add(nums[i]);
trace(list,path,nums,i+1);
path.remove(path.size()-1);
}
}
}
4.排列问题
思路:
(1)确定方法返回值和参数
分析可知遍历数组,不需要返回什么值
参数也就是nums,list,path
(2)确定回溯终止条件
当遍历完数组就结束一次递归
(3)确定单层逻辑
注意这里的for循环从0开始,代表可以重复取。
判断当前数字是否已经在path里面了,是的话就跳过当前数字
否则将当前数字添加进path,继续向下递归,这里需要注意从i+1开始,也就是不可以取自己
当递归返回的时候需要进行回溯,也就是弹出上一个已经使用过的数字
class Solution {
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
List<Integer> path = new ArrayList<Integer>();
used = new boolean[nums.length];
trace(list,path,nums);
return list;
}
public void trace(List list,List path,int[] nums){
if(path.size()==nums.length){
list.add(new ArrayList<Integer>(path));
return;
}
for(int i = 0;i<nums.length;i++){
if(used[i]){
continue;
}
path.add(nums[i]);
used[i] = true;
trace(list,path,nums,i+1);
path.remove(path.size()-1);
used[i] = false;
}
}
}
5.字母大小写全排列
思路:回溯模板
深度:s.length()-数字
宽度:2(小写、大写)
跳过数字,将字母大小写转换,然后进行递归,触底加入结果集。
返回进行回溯,转换大小写,继续递归。
class Solution {
public List<String> letterCasePermutation(String s) {
List<String> list = new ArrayList<>();
trace(list,s.toCharArray(),0);
return list;
}
public void trace(List list,char[] s,int start){
while(start<s.length&&Character.isDigit(s[start])){
start++;
}
if(start == s.length){
list.add(new String(s));
return;
}
s[start] ^= 32;
trace(list,s,start+1);
s[start] ^=32;
trace(list,s,start+1);
}
}
6.单词搜索
思路:
(1)确定方法返回值和参数
分析可知遍需要返回boolean,参数自然需要board、i、j、word、len、cur
(2)确定回溯终止条件
当碰到不符合的字母就返回false代表这一条支路不行,word长度和递归深度一致时代表这条支路可以返回true
(3)确定单层逻辑
从当前的i,j出发,上下左右四个方向可以选择,只要是没用访问过的点和边界内的就进行递归,并且标记当前的点为已使用,补上参数visited
当递归返回的时候需要进行回溯,也就是清除已使用的标记
class Solution {
public boolean exist(char[][] board, String word) {
int h = board.length, w = board[0].length;
boolean[][] visited = new boolean[h][w];
char[] s = word.toCharArray();
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
boolean flag = trace(board,visited,i,j,word.length(),1,s);
if(flag){
return true;
}
}
}
return false;
}
public boolean trace(char[][] board,boolean[][] visited,int i,int j,int len,int cur,char[] word){
if (board[i][j] != word[cur-1]) {
return false;
}
if(len == cur){
return true;
}
visited[i][j] = true;
boolean a=false,b=false,c=false,d=false;
if(i+1<board.length&&visited[i+1][j]==false) {
a = trace(board,visited,i+1,j,len,cur+1,word);
}
if(i-1>=0&&visited[i-1][j]==false){
b = trace(board,visited,i-1,j,len,cur+1,word);
}
if(j+1<board[0].length&&visited[i][j+1]==false) {
c = trace(board,visited,i,j+1,len,cur+1,word);
}
if(j-1>=0&&visited[i][j-1]==false) {
d = trace(board,visited,i,j-1,len,cur+1,word);
}
visited[i][j] = false;
return a|b|c|d;
}
}