参考 手把手教你中的回溯算法
78. 子集
//1.暴力法
//1.暴力法
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
res.add(new ArrayList<>());
for(int num:nums){
int resLength=res.size();
for(int i=0;i<resLength;i++){
List<Integer> cur=new ArrayList<>(res.get(i));
cur.add(num);
res.add(cur);
}
}
return res;
}
//2.dfs
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
res.add(new ArrayList<>());
//回溯算法的模板
List<Integer> track = new ArrayList<>();
backtrack(nums,0,track);
return res;
}
//每一个数都有两种选择:要或不要
private void backtrack(int[] nums,int start,List<Integer> track){
for(int i=start;i<nums.length;i++){
track.add(nums[i]);
res.add(new ArrayList<>(track));
backtrack(nums,i+1,track);
track.remove(track.size()-1);
}
}
90. 子集 II
//1.扩展法
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
res.add(new ArrayList<>());
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
int resLength=res.size();
for(int j=0;j<resLength;j++){
List<Integer> cur=new ArrayList<>(res.get(j));
cur.add(nums[i]);
res.add(cur);
}
}
HashSet<List<Integer>> set=new HashSet<>(res);
return new ArrayList<>(set);
}
//2.深度优先
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
res.add(new ArrayList<>());
//回溯算法的模板
List<Integer> track = new ArrayList<>();
backtrack(nums,0,track);
return res;
}
private void backtrack(int[] nums,int start,List<Integer> track){
for(int i=start;i<nums.length;i++){
//去充条件:i>start&&nums[i]==nums[i-1]
if(i>start&&nums[i]==nums[i-1]) continue;
track.add(nums[i]);
res.add(new ArrayList<>(track));
backtrack(nums,i+1,track);
track.remove(track.size()-1);
}
}
46. 全排列
backtrack的公式:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
List<List<Integer>> res=new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
List<Integer> list=new ArrayList<>();
backtrack(res,list,nums);
return res;
}
public void backtrack(List<List<Integer>> res,
List<Integer> list, int[] nums) {
if(list.size()==nums.length){
res.add(new ArrayList<>(list));
return;
}
for(int num:nums){
if(!list.contains(num)){
list.add(num);
backtrack(res,list,nums);
list.remove(list.size()-1);
}
}
}
47. 全排列 II
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>(); // 记录最终答案
if (nums == null || nums.length == 0) return res;
// visited[i] == 0时,未访问;== 1时,已访问
int[] visited = new int[nums.length];
Arrays.sort(nums); // 排序后容易去重
backTrack(res, nums, new ArrayList<Integer>(), visited);
return res;
}
private void backTrack(List<List<Integer>> res, int[] nums, List<Integer> list, int[] visited) {
// 列表长度为数组长度时,拷贝列表到结果列表中
if (list.size() == nums.length) {
res.add(new ArrayList<>(list));
return ;
}
for (int i = 0; i < nums.length; i++) {
if (visited[i] == 1) continue; // 已访问过,跳过这层循环
// 如果数组相连元素相等,没有先访问后面的元素,就不会存在重复
if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0){
continue;
}
// 加入列表中,标记为已访问,回溯求值
list.add(nums[i]);
visited[i] = 1;
backTrack(res, nums, list, visited);
// 回溯后,重新标记为未访问,删掉最后一个元素
visited[i] = 0;
list.remove(list.size() - 1);
}
}
}
77. 组合
//递归
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
//边界条件判断
if (n < k || k == 0)
return res;
// 选择第n个,从前面n-1个数字中选择k-1个构成一个集合
res = combine(n - 1, k - 1);
//如果res是空,添加一个空的集合
if (res.isEmpty())
res.add(new ArrayList<>());
//然后在前面选择的集合res中的每个子集合的后面添加一个数字n
for (List<Integer> list : res)
list.add(n);
//res中不光要包含选择第n个数字的集合,也要包含不选择第n
//个数字的集合
res.addAll(combine(n - 1, k));
return res;
}
}
//回溯
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> list = new LinkedList<>();
backtrack(list, n, k, 1, new ArrayList<>());
return list;
}
private void backtrack(List<List<Integer>> list, int n, int k, int start, List<Integer> tempList) {
//终止条件,找到一对组合
if (k == 0) {
list.add(new LinkedList<>(tempList));
return;
}
//注意这里的i不能从0开始,如果从0开始会出现重复的,比如[1,2]和[2,1]
for (int i = start; i <= n - k + 1; i++) {
//把当前值添加到集合中
tempList.add(i);
//递归调用,继续下一个分支
backtrack(list, n, k - 1, i + 1, tempList);
//从当前分支跳到下一个分支的时候要把之前添加的值给移除
tempList.remove(tempList.size() - 1);
}
}