题目链接:https://leetcode.com/problems/permutations-ii/description/
题目大意:全排列一串数(有重复数字)。
法一:也即是46题的法一,只加了一行代码。代码如下(耗时499ms):
1 public List<List<Integer>> permute(int[] nums) { 2 List<List<Integer>> list = new ArrayList<List<Integer>>(); 3 int[] vis = new int[nums.length]; 4 Arrays.fill(vis, 0);//初始化全为0 5 List<Integer> tmp = new ArrayList<Integer>(); 6 dfs(list, nums, vis, tmp); 7 return list; 8 } 9 public static void dfs(List<List<Integer>> list , int[] nums, int[] vis, List<Integer> tmp) { 10 // System.out.println(tmp); 11 if(tmp.size() == nums.length) { 12 if(!list.contains(tmp)) 13 list.add(new ArrayList<Integer>(tmp));//这里一定要用new之后再加入到list中,否则list集合会是空值 14 return; 15 } 16 for(int i =0 ; i < nums.length; i++) { 17 if(vis[i] == 0) { 18 vis[i] = 1; 19 tmp.add(nums[i]); 20 dfs(list, nums, vis, tmp); 21 tmp.remove(tmp.size() - 1);//删除最后一个元素 22 vis[i] = 0; 23 } 24 } 25 }
法二:也就是46题的法二,只加了一行代码,利用List特性判重:list.contains()。代码如下(耗时78ms):
1 public List<List<Integer>> permute(int[] nums) { 2 LinkedList<List<Integer>> res = new LinkedList<List<Integer>>(); 3 res.add(new ArrayList<Integer>());//加一个空的list进去,以保证下面size在第一次的时候就为1,从而保证后面的计算 4 for(int n : nums) {//遍历nums数组值 5 for(int size = res.size(); size > 0; size--) {//查看结果集res中已经包含几个序列 6 List<Integer> tmp = res.pollFirst();//取出结果集res中的每个序列 7 for(int i = 0; i <= tmp.size(); i++) {//对于取出的序列,在每一个位置都尝试加入当前nums数组值,这里的i表示当前取出序列的第i位,比如取出的序列是1,接下来就要在1之前及1之后加入2 8 List<Integer> resIn = new ArrayList<Integer>(tmp);//每一个位置都新初始化刚取出的序列,这样保证能在原始取出序列的基础上增添数值 9 resIn.add(i, n);//在第i位加入数值n 10 // System.out.println(resIn); 11 if(res.contains(resIn)) continue; 12 res.add(resIn);//这里因为每从结果集中取出一个序列就是清空一个序列,所以这里add的时候就是一个全新的序列 13 } 14 } 15 } 16 return res; 17 }
剪枝:前面的都是把所有的排列都计算完之后再考虑是否加入结果集中,而下面这个是在计算排列的时候就进行剪枝。这里剪枝利用前后元素的相等性来判断剪枝。正确的剪枝真的可以减少好多的时间复杂度。具体如下(耗时9ms):
1 public List<List<Integer>> permuteUnique(int[] nums) { 2 List<List<Integer>> list = new ArrayList<List<Integer>>(); 3 int[] vis = new int[nums.length]; 4 Arrays.fill(vis, 0);//初始化全为0 5 List<Integer> tmp = new ArrayList<Integer>(); 6 Arrays.sort(nums);//一定要先排序,这样才能保证下面剪枝的时候,相同的元素相邻 7 dfs(list, nums, vis, tmp); 8 return list; 9 } 10 public static void dfs(List<List<Integer>> list , int[] nums, int[] vis, List<Integer> tmp) { 11 // System.out.println(tmp); 12 if(tmp.size() == nums.length) { 13 list.add(new ArrayList<Integer>(tmp));//这里一定要用new之后再加入到list中,否则list集合会是空值 14 return; 15 } 16 for(int i =0 ; i < nums.length; i++) { 17 //套路剪枝,去除重复元素 18 //选取下一个元素的时候,要查看这个元素的前一个元素是否和它相同,如果相同而且没有使用过,就不用选取这个元素,因为如果选取了这个元素,所得的结果被包含于选取了前一个相同元素的结果中。 19 if(i > 0 && vis[i - 1] == 0 && nums[i - 1] == nums[i]) { 20 continue; 21 } 22 else if(vis[i] == 0){ 23 vis[i] = 1; 24 tmp.add(nums[i]); 25 dfs(list, nums, vis, tmp); 26 tmp.remove(tmp.size() - 1);//删除最后一个元素 27 vis[i] = 0; 28 } 29 } 30 }