回溯算法
回溯算法模版
private void backtrack("原始参数") {
//终止条件(递归必须要有终止条件)
if ("终止条件") {
//一些逻辑操作(可有可无,视情况而定)
return;
}
for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
//一些逻辑操作(可有可无,视情况而定)
//做出选择
//递归
backtrack("新的参数");
//一些逻辑操作(可有可无,视情况而定)
//撤销选择
}
}
问题1字符串无重复选取
剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
示例:
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
限制:
1 <= s 的长度 <= 8
通过次数100,112
提交次数177,682
套入框架
class Solution {
public String[] permutation(String s) {
Set<String> res = new HashSet<>();
back(s.toCharArray(),"",new boolean[s.length()],res);
//toArray转为object类型的数组,要想转为指定类型的数组,需要我们new String[0] 注意里面的字符最大是0-res.size(),再多会补null;
return res.toArray(new String[res.size()]);
}
private void back(char[] chars,String tmp,boolean[] visited,Set<String> res){
//首先是终止条件
if(chars.length == tmp.length()){
//一些所需要的操作
res.add(tmp);
return;
}
for(int i=0;i<chars.length;i++){
//排除一些选项或者跳过某些东西
if(visited[i]) continue;
//做出选择
visited[i] =true;
//回溯
back(chars,tmp+chars[i],visited,res);
//撤销选择
visited[i] = false;
}
}
}
问题2数组无重复选取
46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
套入框架
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new LinkedList<>();
//回溯四项,第一个是原来的数据集,第二个变量为了建立1,0(表示有没有走过),1表示走过,就不再走了),
// 然后建立一个一维的list存贮当前元素,res存储最终结果
back(nums,new int[nums.length],new LinkedList<Integer>(),res );
return res;
}
private void back(int[] nums,int[] visited,LinkedList<Integer> tmp,List<List<Integer>> res){
//终止条件
if(nums.length == tmp.size()){
res.add(new LinkedList(tmp));
return;
}
//开始for循环遍历
for(int i=0;i<nums.length;i++){
//跳过某些元素
if(visited[i] ==1) continue;
//做出选择
visited[i] =1;
//回溯
tmp.add(nums[i]);
back(nums,visited,tmp,res);
//撤销选择
visited[i] =0;
tmp.removeLast();
}
}
}
问题3数组重复选取(和无重复元素选取有一定不同)
39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
套入框架
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new LinkedList<>();
if(candidates.length ==0 ) return res;
// 五个元素,第一个变量是原有数组,第二个变量为了保证i是一个可以重复选取的值,第三个是目标变化,第四个存放单个最终结果,最后一个变量存放最终结果
dfs(candidates,0,target,new LinkedList<>(),res);
return res;
}
public void dfs(int[] candidates,int start,int target,LinkedList<Integer> tmp,List<List<Integer>> res){
//终止条件
if(target<0) return;
if(target ==0){
res.add(new LinkedList<Integer>(tmp));
return;
}
for(int i =start;i<candidates.length;i++){
//重复元素与不重复元素的区别,重复元素,不需要做出选择和撤销选择,重复元素需要建立 1,0或者true或者false来跳过元素
tmp.add(candidates[i]);
dfs(candidates,i,target-candidates[i],tmp,res);
tmp.removeLast();
}
}
}
总结
回溯算法还是背模版好这样可以做到事半功倍。