1. 回溯算法1 https://leetcode-cn.com/problems/permutations/
import java.util.ArrayList; import java.util.List; /** 题目描述:给定一个 没有重复 数字的序列,返回其所有可能的全排列。 No77题目:返回n个数中k个数的组合 本题中每个排列都要用到所有的数字 方法1:每一次选择两个数ij进行交换,for (int j = i; j <nums.length ; j++) 从i位置开始进行回溯 方法2:采用一个visited[]数组标记已经访问过的数字,for(int i=0;i<n;i++)从0进行回溯 */ public class No46_permute { public static void main(String[] args) { int[] arr={1,2,3}; List<List<Integer>> lists=new ArrayList<>(); lists=permute(arr); for (int i = 0; i <lists.size() ; i++) { System.out.println(lists.get(i)); } } public static List<List<Integer>> permute(int[] nums) { if (nums==null||nums.length==0) return null; List<List<Integer>> lists=new ArrayList<>(); List<Integer> list=new ArrayList<>(); return dfs(nums,0,list,lists); } private static List<List<Integer>> dfs(int[] nums, int i, List<Integer> list, List<List<Integer>> lists) { if (i>nums.length) return lists; if (i==nums.length){ lists.add(new ArrayList<>(list)); return lists;} for (int j = i; j <nums.length ; j++) { swap(nums,i,j);//先交换再add。 // j=i时,数组为【1,2,3】完成一次dfs,可以获得1,2,3和1,3,2; // 然后j=i+1,应该先交换i和j,得到数组【2,1,3】,再进行dfs,得到2,1,3和2,3,1;如果先add,再交换,则与之前的重复,交换失去了意义 list.add(nums[i]); dfs(nums,i+1,list,lists); list.remove(list.size()-1); swap(nums,i,j);//每一次都应该换回交换前的状态,保证下次交换时,只与最初状态差一,否则将差太多 } return lists; } private static void swap(int[] nums, int i, int j) { int temp=nums[i]; nums[i]=nums[j]; nums[j]=temp; } /** ++++++++++++++++++++++++++++ 方法2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ public List<List<Integer>> permute2(int[] nums) { // 首先是特判 int len = nums.length; // 使用一个动态数组保存所有可能的全排列 List<List<Integer>> res = new ArrayList<>(); if (len == 0) { return res; } boolean[] used = new boolean[len]; List<Integer> path = new ArrayList<>(); dfs(nums, len, 0, path, used, res); return res; } private void dfs(int[] nums, int len, int depth, List<Integer> path, boolean[] used, List<List<Integer>> res) { if (depth == len) { res.add(path); return; } for (int i = 0; i < len; i++) { if (!used[i]) { path.add(nums[i]); used[i] = true; dfs(nums, len, depth + 1, path, used, res); // 注意:这里是状态重置,是从深层结点回到浅层结点的过程,代码在形式上和递归之前是对称的 used[i] = false; path.remove(path.size() - 1); } } } }
2.回溯算法2 https://leetcode-cn.com/problems/permutations-ii/
import java.util.*; /** * 题目描述:给定一个可包含重复数字的序列,返回所有不重复的全排列。 * dfs前,需要进行去重 */ public class No47_permuteUnique { public List<List<Integer>> permuteUnique(int[] nums) { int len = nums.length; List<List<Integer>> res = new ArrayList<>(); if (len == 0) { return res; } // 排序(升序或者降序都可以),排序是剪枝的前提 Arrays.sort(nums); boolean[] used = new boolean[len]; // 使用 Deque 是 Java 官方 Stack 类的建议 Deque<Integer> path = new ArrayDeque<>(len); dfs(nums, len, 0, used, path, res); return res; } private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) { if (depth == len) { res.add(new ArrayList<>(path)); return; } for (int i = 0; i < len; ++i) { if (used[i]) continue; // 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义 // 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择 if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) { continue; } path.addLast(nums[i]); used[i] = true; dfs(nums, len, depth + 1, used, path, res); // 回溯部分的代码,和 dfs 之前的代码是对称的 used[i] = false; path.removeLast(); } } }
3.组合 https://leetcode-cn.com/problems/combinations/
import java.util.ArrayList; import java.util.List; //题目描述:给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 //思路:采用dfs 回溯 /** * 分析: * 1.返回结果:List<List<Integer>> * 2.dfs工具:第一步肯定要传入n,k,lists,另外还有一个index表示当前递归到哪一个数了 * 2.1 递归终止条件:递归了k次 * 2.2 从当前数字开始进行递归,每一次尝试添加数字,进行dfs * 2.3 不管成功与否都进行回溯 */ public class No77_combinations { public static void main(String[] args) { List<List<Integer>> lists; lists = new Solution77().combine(4, 2); System.out.println(lists); } } class Solution77 { public List<List<Integer>> combine(int n, int k) { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); dfs(n, k, 0, list,lists); return lists; } public void dfs(int n, int k, int index, List<Integer> list,List<List<Integer>> lists) { if (k == 0) { // lists.add(list);//---有问题 lists.add(new ArrayList<>(list)); return ; } // for (int i = index; i < n; i++) { for (int i = index; i < n-k+1; i++) {//剪枝 剪枝前后,用时打败前50%和100% list.add(i + 1);//尝试添加 dfs(n, k - 1, i + 1, list,lists); list.remove(list.size() - 1);//回溯,成功与否均进行回溯 } } }
4.subset https://leetcode-cn.com/problems/subsets/
import java.util.ArrayList; import java.util.List; public class No78_subSets { class Solution { List<List<Integer>> output = new ArrayList(); int n, k; /** * 每一轮,与No46类似。不同之处在于,不要交换,因为交换后的1,2,3和1,3,2在这里看做是重复的 * @param first * @param curr * @param nums */ public void backtrack(int first, ArrayList<Integer> curr, int[] nums) { if (curr.size() == k) output.add(new ArrayList(curr)); for (int i = first; i < n; ++i) { curr.add(nums[i]); backtrack(i + 1, curr, nums); curr.remove(curr.size() - 1);//回溯的目的是找到所有满足长度为k的组合 } } public List<List<Integer>> subsets(int[] nums) { n = nums.length; // 【1,2,3】为例 // 第一次是长度为0,空集 // 第二次是长度为1,1或2或3.。。。 for (k = 0; k < n + 1; ++k) { backtrack(0, new ArrayList<Integer>(), nums); } return output; } } }