【回溯-全排列&&dfs】leet-47. 全排列
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
此题同:【回溯-全排列】leet-46. 全排列区别在于第46题没有重复数字,本题出现了重复数字,如果直接用46的方式会出现重复的排列。所以在dfs的选择过程中需要去重。
基于46题的方法一,在构建方案的第 i 位,需要从【i,n-1】上选择数字和第i位进行置换,在这个过程中 如果【i,n-1]区间有重复数字,此时swap之后选择第i 位就出现了重复选择。同一个数字在第i 位被选择了多次。
为避免重复选择,使用HashSet<Integer> 保存已经被选择过的数字,后面如果出现了重复,则直接跳过出现重复的数字的选择方案。
代码:
public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> paths=new ArrayList<>(); dfs(nums,0,new ArrayList<>(),paths); return paths; } public void dfs(int[] nums,int i,List<Integer> path,List<List<Integer>> paths){ if(i==nums.length){ paths.add(new ArrayList<>(path)); return; } HashSet<Integer> set=new HashSet<>(); for(int k=i;k<nums.length;k++){ if(set.contains(nums[k])) continue; set.add(nums[k]); swap(nums,i,k); path.add(nums[i]); dfs(nums,i+1,path,paths); path.remove(path.size()-1); swap(nums,i,k); } } public void swap(int[] nums,int i,int j){ int t=nums[i]; nums[i]=nums[j]; nums[j]=t; }
基于46题的方法二, 则需要理解设置第i位的时候,相同的数字在这个位置只能设置1次,所以需要排序数组,
在用第k位设置 i 位置的数字的时候,如果它后面有与它相同的数字,这些数字的选择方案直接被过滤掉
代码:
public List<List<Integer>> permuteUnique(int[] nums) { Arrays.sort(nums); int n=nums.length; boolean[] f=new boolean[n]; List<List<Integer>> ans=new ArrayList<>(); dfs(nums,new ArrayList<>(),f,ans); return ans; } public void dfs(int[] nums,List<Integer> path,boolean[] u,List<List<Integer>> ans){ if(path.size()==nums.length){ ans.add(new ArrayList<>(path)); return; } for(int k=0;k<nums.length;){ if(u[k]){ k++; continue; } u[k]=true; path.add(nums[k]); dfs(nums,path,u,ans); path.remove(path.size()-1); u[k]=false; int kval=nums[k]; while(k<nums.length&&nums[k]==kval) k++; //过滤重复 } }
也可以不排序通过HashSet数组来过滤重复,所以以下也是可以:
public List<List<Integer>> permuteUnique(int[] nums) { int n=nums.length; boolean[] f=new boolean[n]; List<List<Integer>> ans=new ArrayList<>(); dfs(nums,new ArrayList<>(),f,ans); return ans; } public void dfs(int[] nums,List<Integer> path,boolean[] u,List<List<Integer>> ans){ if(path.size()==nums.length){ ans.add(new ArrayList<>(path)); return; } HashSet<Integer> set=new HashSet<>(); for(int k=0;k<nums.length;k++){ if(u[k] || set.contains(nums[k])){ continue; } set.add(nums[k]); u[k]=true; path.add(nums[k]); dfs(nums,path,u,ans); path.remove(path.size()-1); u[k]=false; } }
相似题目:
算法笔试题解-回溯系列 文章被收录于专栏
算法笔试题解-回溯系列