回溯算法公式:
class solution{
*定义一些全局变量,如集合
public 返回类型 方法名(参数){
1.*一些逻辑操作
2.dfs(参数);
2.return 结果 ;
}
public void(返回类型)dfs(参数){
1.一些逻辑操作
2.*终止条件
if(终止条件判断){
return ;
}
3.回溯递归
for(){
3.1. 做选择
if(){
}
3.2. 一些逻辑操作
3.3. *开启递归
dfs()
3.4. *撤销选择
}
}
}
例题一:子集
给定一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path= new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
dfs(nums,0);
return res;
}
public void dfs(int[] nums,int start){
//逻辑操作
res.add(new LinkedList(path));//将遍历的路径加入res集合
//终止条件
if(nums.length==0 ||start>nums.length-1){
return ;
}
//回溯遍历结果
for(int i=start;i<nums.length;i++){
path.add(nums[i]);
dfs(nums,i+1);//进行回溯遍历
path.removeLast();//路径回撤
}
}
}
图解链接:LeetCode详解
例题二:子集II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path =new LinkedList<>();
boolean[] visted;
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
visted=new boolean[nums.length];
dfs(nums,0);
return res;
}
public void dfs(int[] nums, int start){
//逻辑操作
res.add(new LinkedList(path));
//终止条件
if(nums.length==0 || start>nums.length-1){
return ;
}
//回溯遍历
for(int i=start;i<nums.length;i++){
//做选择->树层去重
if(i>0 && nums[i]==nums[i-1] && visted[i-1]==false){
continue;
}
path.add(nums[i]);
visted[i]=true;
dfs(nums,i+1);
path.removeLast();
visted[i]=false;
}
}
}
图解链接:LeetCode详解
例题三:组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例 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]]
示例 3:
输入: candidates = [2], target = 1
输出: []
未剪枝代码示例:
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path =new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(candidates,target,0,0);
return res;
}
public void dfs(int[] candidates,int target,int sum,int start){
//逻辑操作
if(sum==target){
res.add(new LinkedList(path));
return ;
}
//终止条件
if(sum>target || start>candidates.length-1){
return ;
}
//回溯遍历
for(int i=start;i<candidates.length;i++){
path.add(candidates[i]);
dfs(candidates,target,sum+candidates[i],i);
path.removeLast();
}
}
}
剪枝后代码示例:
通过排序数组后由if(sum+candidates[i]>target) break; 剪枝
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path =new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0,0);
return res;
}
public void dfs(int[] candidates,int target,int sum,int start){
//逻辑操作
if(sum==target){
res.add(new LinkedList(path));
return ;
}
//终止条件
if(start>candidates.length-1){
return ;
}
//回溯遍历
for(int i=start;i<candidates.length;i++){
if(sum+candidates[i]>target)
break;
path.add(candidates[i]);
dfs(candidates,target,sum+candidates[i],i);
path.removeLast();
}
}
}
例题四:组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
class Solution {
List<List<Integer>> res =new ArrayList<>();
LinkedList<Integer> path =new LinkedList<>();
boolean[] visited;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
visited=new boolean[candidates.length];
Arrays.sort(candidates);
dfs(candidates,target,0,0);
return res;
}
public void dfs(int[] candidates,int target,int start,int sum){
//逻辑操作
if(sum==target){
res.add(new LinkedList(path));
return ;
}
//终止条件
if(sum>target || start>candidates.length-1)
return ;
//回溯遍历
for(int i=start;i<candidates.length;i++){
//剪枝
if(sum+candidates[i]>target)
break;
//去重
if(i>0 && candidates[i]==candidates[i-1] && visited[i-1]==false)
continue;
path.add(candidates[i]);
visited[i]=true;
dfs(candidates,target,i+1,sum+candidates[i]);
visited[i]=false;
path.removeLast();
}
}
}