【回溯-全排列&&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;

            }
    }

相似题目:

【回溯-全排列&&dfs】leet-46. 全排列

【回溯-全排列&&dfs】leet-47. 全排列

【回溯-全排列&&dfs】BM56 有重复项数字的全排列

【回溯-全排列&&dfs】OD100. 基站维修工程师

【回溯-全排列&&dfs】蜜蜂采蜜

算法笔试题解-回溯系列 文章被收录于专栏

算法笔试题解-回溯系列

全部评论

相关推荐

不愿透露姓名的神秘牛友
06-19 20:55
因为业务不是喜欢的,所以就没去,现在实习工作也有很多dirtywork,很后悔,怎么能舔回这个offer啊
flmz_Kk:试一试跟hr舔回来,不过保不齐米的活也有很多dirtywork,只能说不要美化自己没走过的路
点赞 评论 收藏
分享
04-25 19:29
已编辑
宁波大学 测试开发
被普调的六边形战士很高大:你我美牛孩
点赞 评论 收藏
分享
后来123321:别着急,我学院本大二,投了1100份,两个面试,其中一个还是我去线下招聘会投的简历,有时候这东西也得看运气
点赞 评论 收藏
分享
24分钟1.自我介绍2.黑盒测试用例设计方法3.运用刚才的测试方法对手机端淘宝购物车结算页面进行测试4.测试流程5.需求文档没有标明边界值,怎么确定边界值,确定边界值后怎么测6.你们公司自动化测试是测业务主流程还是新需求反问:不足之处答:问答问题前思考3s再答,针对提问再答
一笑而过2222:边:边界值分析法(处理输入边界) 类:等价类划分法(划分有效 / 无效输入) 定:判定表法(多条件组合的逻辑判定) 因:因果图法(分析输入输出的因果关系) 迁:状态迁移法(覆盖系统状态转换路径) 场:场景法(模拟端到端业务流程) 正:正交试验法(多因素组合的测试优化) 错:错误推测法(基于经验推测潜在漏洞) 记忆逻辑链(按测试场景优先级排序) 先处理明确输入:边界值 + 等价类(边类) 再处理条件组合:判定表 + 因果图(定因) 接着处理状态与流程:状态迁移 + 场景法(迁场) 最后优化多因素与补漏:正交试验 + 错误推测(正错)
查看6道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务