回溯算法的模板
List<Object> list = new ArrayList<>();
public void backtrack(路径,选择列表){
//设置结束条件
if 满足结束条件{
lista.dd(路径);
return;
}
//候选节点的选择
for(int i=start;i<选择列表的长度;i++){
//做选择 是否要将当前节点添加到list中
list.add(nums[i]);
//开启下一轮的遍历
backtrack(路径,选择列表);
//撤销选择
list.remove(list.size()-1);
}
}
下面做一些leetcode典型的使用回溯解决的算法题。
leetcode 46. 全排列
题目描述:给定一个 没有重复 数字的序列,返回其所有可能的全排列。
class Solution {
public List<List<Integer>> list; //保存最终结果
public List<Integer> cur; //保存当前路径
public List<List<Integer>> permute(int[] nums) {
list = new ArrayList<>();
cur = new ArrayList<>();
boolean[] flag = new boolean[nums.length]; //判断当前元素是否已经被访问过了
backtrack(nums,list,cur,flag);
return list;
}
public void backtrack(int[] nums,List<List<Integer>> list,List<Integer> cur,boolean[] flag){
//设置结束条件
if(cur.size()==nums.length){
list.add(new ArrayList(cur));
return;
}
//候选节点的选择
for(int i=0;i<nums.length;i++){
if(flag[i]==false){
//如果当前元素没有被访问过
cur.add(nums[i]);
flag[i]=true;
//进行下一步的遍历
backtrack(nums,list,cur,flag);
//撤销操作
flag[i]=false;
cur.remove(cur.size()-1);
}
}
}
}
leetcode 47. 全排列 II
题目描述:给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
这道题和上面一道题的区别就是这道题中nums包含重复数字,只要在将cur添加在list里面的时候,判断一下是否list中包含了cur。
class Solution {
public List<List<Integer>> list; //保存最终结果
public List<Integer> cur; //保存当前路径
public List<List<Integer>> permuteUnique(int[] nums) {
list = new ArrayList<>();
cur = new ArrayList<>();
boolean[] flag = new boolean[nums.length]; //判断当前元素是否已经被访问过了
backtrack(nums,list,cur,flag);
return list;
}
public void backtrack(int[] nums,List<List<Integer>> list,List<Integer> cur,boolean[] flag){
//设置结束条件
if(cur.size()==nums.length){
if(!list.contains(cur)){
//确保不重复
list.add(new ArrayList(cur));
}
return;
}
//候选节点的选择
for(int i=0;i<nums.length;i++){
if(flag[i]==false){
//如果当前元素没有被访问过
cur.add(nums[i]);
flag[i]=true;
//进行下一步的遍历
backtrack(nums,list,cur,flag);
//撤销操作
flag[i]=false;
cur.remove(cur.size()-1);
}
}
}
}
leetcode 78. 子集
题目描述:
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
class Solution {
public List<List<Integer>> list; //最终结果
public List<Integer> cur; //当前子集列表
public List<List<Integer>> subsets(int[] nums) {
list = new ArrayList<>();
cur = new ArrayList<>();
list.add(cur); //首先将空集加入
dfs(0,nums,list,cur);
return list;
}
public void dfs(int index,int[] nums,List<List<Integer>> list,List<Integer> cur){
//设置终止条件
if(index==nums.length){
return;
}
//候选节点的选择
for(int i=index;i<nums.length;i++){
//进行选择
cur.add(nums[i]);
list.add(new ArrayList(cur));
dfs(i+1,nums,list,cur);
//撤销选择
cur.remove(cur.size()-1);
}
}
}
leetcode 90. 子集 II
题目描述:
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
这道题比上一道题多了去重。
class Solution {
public List<List<Integer>> list; //最终结果
public List<Integer> cur; //当前子集列表
public List<List<Integer>> subsetsWithDup(int[] nums) {
//首先对nums进行排序
Arrays.sort(nums);
List<List<Integer>> list = new ArrayList<>();
List<Integer> cur = new ArrayList<>();
list.add(cur); //首先将空集加入
dfs(nums,0,list,cur);
return list;
}
public void dfs(int[] nums,int start,List<List<Integer>> list,List<Integer> cur){
//设立结束条件
if(start==nums.length){
return;
}
for(int i=start;i<nums.length;i++){
//去重
if(i>start && nums[i]==nums[i-1]) continue;
//做出选择
cur.add(nums[i]);
list.add(new ArrayList(cur));
dfs(nums,i+1,list,cur);
//撤销选择
cur.remove(cur.size()-1);
}
}
}
leetcode77. 组合
题目描述:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
这道题其实类似于求子集,不过是限定长度的子集。
class Solution {
public List<List<Integer>> list; //最终结果
public List<Integer> cur; //当前子集列表
public List<List<Integer>> combine(int n, int k) {
list = new ArrayList<>();
cur = new ArrayList<>();
dfs(n,k,1,list,cur);
return list;
}
public void dfs(int n,int k,int start,List<List<Integer>> list,List<Integer> cur){
//结束条件
if(k==cur.size()){
list.add(new ArrayList(cur));
return;
}
//候选节点
for(int i=start;i<=n;i++){
//做出选择
cur.add(i);
dfs(n,k,i+1,list,cur);
//撤销选择
cur.remove(cur.size()-1);
}
}
}
leetcode 39. 组合总和
题目描述:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
class Solution {
public List<List<Integer>> list; //保存最终结果
public List<Integer> cur; //当前列表
public List<List<Integer>> combinationSum(int[] candidates, int target) {
list = new ArrayList<>();
cur = new ArrayList<>();
int cursum = 0; //表示当前列表中的和
backtrack(candidates,target,list,cur,cursum);
return list;
}
public void backtrack(int[] candidates, int target,List<List<Integer>> list,List<Integer> cur,int cursum){
//设置结束条件
if(cursum>=target){
if(cursum==target){
//对cur进行一个排序
List<Integer> temp = new ArrayList<>(cur);
Collections.sort(temp);
//题目要求解集中不能包含重复的组合 [2,2,3]和[3,2,2]算重复
if(!list.contains(temp)){
list.add(temp);
}
}
return;
}
//候选节点的选择
for(int i=0;i<candidates.length;i++){
//做出选择
cur.add(candidates[i]);
cursum+=candidates[i];
backtrack(candidates,target,list,cur,cursum);
//撤销选择
cursum-=candidates[i];
cur.remove(cur.size()-1);
}
}
}
leetcode 17. 电话号码的字母组合
以下为Java代码实现:
class Solution {
public List<String> letterCombinations(String digits) {
List<String> list = new ArrayList<>();
if(digits.length()==0){
return list;
}
String[] strs = {"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
list.add("");
for(int i=0;i<digits.length();i++) {
List<String> temp = new ArrayList<String>();
for(int j=0;j<strs[digits.charAt(i)-'2'].length();j++) {
for(int k=0;k<list.size();k++) {
temp.add(list.get(k)+strs[digits.charAt(i)-'2'].charAt(j));
}
}
list = temp;
}
return list;
}
}
还有非常多类似的题,学着做一下总结,回溯太难了,任重道远。