全排列系列

46. 全排列

给定一个不含重复数字的数组,返回所有可能的全排列

力扣传送门

思路:DFS
解析来自算法书《算法笔记》——胡凡
算法笔记

 List<List<Integer>> ans = new ArrayList<>();
 	
 public List<List<Integer>> permute(int[] nums) {
     boolean[] hash = new boolean[nums.length];
     Integer[] tmp= new Integer[nums.length];
     digui(nums,hash,tmp,0);
     return ans;
 }
 //递归,index可理解为到达递归第几层,代表第几个排列数(从0开始算)
 public void digui(int[] nums,boolean[] hash,Integer[] tmp,int index) {        
     //边界
     if(index == nums.length) {
         //手动循环
         List<Integer> list = new ArrayList<>();        
         for(Integer a : tmp) {
             list.add(a);
         }
         ans.add(list);
         return;
     }
     int l = nums.length;
     for(int i =0 ;i < l; i++) {
         if(hash[i] == false) {
             tmp[index] = nums[i];
             hash[i] = true;
             digui(nums,hash,tmp,index+1);
             hash[i] = false;
         }
     }
     return;
 }

边界注意点:引用问题
保存排列结果时,如果使用asList会出现引用问题。

 if(index == nums.length) {
      //tmp传进去的是引用,会导致ans最后所有数组都是一样的,因为tmp仅记录最新值
      List<Integer> list = Arrays.asList(tmp);
      return;
  }

引用问题举例

    @Test
    public void test1(){
        Integer[] nums = new Integer[3];
        nums[0] =1;
        nums[1] =2;
        nums[2] =3;
        //传进去的只是数组引用
        List<Integer> list1 = Arrays.asList(nums);
        nums[0] =3;
        nums[1] =1;
        nums[2] =2;
        List<Integer> list2 = Arrays.asList(nums);
        System.out.println(list1);//[3, 1, 2]
        System.out.println(list2);//[3, 1, 2]
    }
    @Test
    public void test2(){
        Integer[] nums = new Integer[3];
        nums[0] =1;
        nums[1] =2;
        nums[2] =3;
        List<Integer> list1 = new ArrayList<>();
        // 这种添加,是元素本身传进去
        for(int i = 0;i < 3;i++){
            list1.add(nums[i]);
        }
        nums[0] =3;
        nums[1] =1;
        nums[2] =2;
        List<Integer> list2 = new ArrayList<>();
        for(int i = 0;i < 3;i++){
            list2.add(nums[i]);
        }
        System.out.println(list1);//[1, 2, 3]
        System.out.println(list2);//[3, 1, 2]
    }
47. 全排列 II

同上,只是数组可存在重复元素

力扣传送门

思路一:DFS + HashSet
由于排列存在重复数字,这就导致排列存在重复,使用Set集合可排除重复的排列,实现很简单,跟全排列没差,但效率很慢。

class Solution {
    
    private Set<List<Integer>> set; // 存放所有排列
    private Integer[] arr; //存放每次的排列
    private boolean[] hash;

    public List<List<Integer>> permuteUnique(int[] nums) {
        set = new HashSet<>();
        arr = new Integer[nums.length];
        hash = new boolean[nums.length];
        dfs(nums,0);
        List<List<Integer>> res = new ArrayList<>(set);
        return res;
    }   
    
    public void dfs(int[] nums, int index) {    
        int l = nums.length;
        if(index == l) {            
            List<Integer> list = new ArrayList<>();
            for(Integer a : arr) {//手动for--引用问题
                list.add(a);
            }
            set.add(list); 
            return;
        }
        for(int i=0; i<l; i++) {
            if(hash[i]) continue;
            hash[i] =  true;
            arr[index] = nums[i];
            dfs(nums, index+1);
            hash[i] =  false;
        }
    }
}

思路二:DFS+快排(巧用下标)
先对数组排序,使得重复的数字能挤到一块,例如1211,排序后为1112,要保证排列不重复,只需保证重复数字(111)的先后顺序不发生改变即可
例如,访问nums[1]时,需要保证nums[0]被访问过,也就是hash[0]==true,否则nums[0]必定排在nums[1]后面,也就是顺序乱了,同理访问nums[2]需要保证nums[1]被访问过。对于nums[0]和nums[3],前面无重复的数,不用判断。如何判断有没有重复,最简单的就是跟前一个数做比较(排序的原因),如何判断有没有序,重复的前提下,若前一个数未访问过,则直接跳过,减少排列的重复运算。

class Solution {
    
    private List<List<Integer>> res; // 存放所有排列
    private Integer[] arr; //存放每次的排列
    private boolean[] hash; //记录是否访问

    public List<List<Integer>> permuteUnique(int[] nums) {
        res = new ArrayList<>();
        arr = new Integer[nums.length];
        hash = new boolean[nums.length];
        Arrays.sort(nums); //快排
        dfs(nums,0);        
        return res;
    }   
    
    public void dfs(int[] nums, int index) {   //index
        int l = nums.length;
        if(index == l) {            
            List<Integer> list = new ArrayList<>();
            for(Integer a : arr) {//手动for--引用问题
                list.add(a);
            }
            res.add(list); 
            return;
        }
        for(int i=0; i<l; i++) {
            if(hash[i]) continue;
            if(i>0 && nums[i]==nums[i-1] && !hash[i-1]) continue; //存在重复,且未访问过
            hash[i] =  true;
            arr[index] = nums[i];
            dfs(nums, index+1);
            hash[i] =  false;
        }
    }
}

同类题目:剑指 Offer 38. 字符串的排列

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值