LeetCode.46(47,31) Permutations && II && Next Permutation(经典的对数列求组合问题)

题目46:

Given a collection of distinct numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:

[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

分析:

答案1:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        //给定数组有多少中排序方式
        //使用递归实现
        
        List<List<Integer>> list=new ArrayList<>();
        backtrack(list,new ArrayList<>(),nums);
        return list;
        
    }
    public void backtrack(List<List<Integer>> list,List<Integer> tempList,int [] nums){
        if(tempList.size()==nums.length){
            list.add(new ArrayList<>(tempList));
        }else{
            
            for(int i=0;i<nums.length;i++){
                //跳过同样的,不执行后面的语句
                if(tempList.contains(nums[i])) continue;
                
                tempList.add(nums[i]);
                //递归
                backtrack(list,tempList,nums);
                tempList.remove(tempList.size()-1);
               
            }
        }       
    }
}


答案2:

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list=new ArrayList<List<Integer>>();
        List<Integer> sub=new ArrayList<Integer>();
        //给定数组有多少中排序方式
        //使用递归实现
        generate(list,sub,nums,0);
        return list;
        
    }
    private void generate(List<List<Integer>>list,List<Integer> sub,int[] nums,int n){
        //出口,满足一个数组
        if(n==nums.length){
            list.add(sub);
            return ;
        }
        for(int i=0;i<=sub.size();i++){
            //初始化一个sub一样的长度的temp集合
            List<Integer> temp=new ArrayList<Integer>(sub);
            temp.add(i,nums[n]);
            //递归求剩下的字符串
            generate(list,temp,nums,n+1);
        }
        return ;
    }
}

题目47:

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:

[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

分析1:

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        //给定数组,存在重复数字
        //思路:类似premutations,先对数组进行排序,这样是为了跳过重复数据,这里需要设置一个flag来判断是否访问过,如果已经访问过则不再访问
        List<List<Integer>> list=new ArrayList<>();
        if(nums.length==0||nums==null) return list;
        boolean [] flag=new boolean[nums.length];
        Arrays.sort(nums);
        
        backtrace(list,new ArrayList<>(),nums,flag);
        return list;
    }
    
    public void backtrace(List<List<Integer>> list,List<Integer> subList,int [] nums,boolean [] flag){
        if(subList.size()==nums.length){
            list.add(new ArrayList<>(subList));
        }else{
            for(int i=0;i<nums.length;i++){
                //去重复(如果访问过或者相等,前面相同的没有访问过)
                if(flag[i]||(i>0&&nums[i]==nums[i-1]&&!flag[i-1])) continue;
                //将其标记已经使用了
                flag[i]=true;
                //递归实现
                subList.add(nums[i]);
                backtrace(list,subList,nums,flag);
                //用完,将其标记置回未使用
                flag[i]=false;
                subList.remove(subList.size()-1);
            }
        }
    }
}

分析2(最优算法(无需对数组进行排序),推荐):

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        //对于存在重复数据的数组求解所有可能的组合
        //思路:最优算法:因为存在重复数据,所以需要设置一个标志位来之前是否已经访问同样的数据。每次固定第一个位置,之后对后面n-1个数据递归求组合
        //关键步骤:假设需要判断下标为k(k>0)的数据,那么我们将k和0进行调换,(同时在0-k这里面查找是否存在相同的,如果不存在则是新的组合)当递归完后,再换回来。
        List<List<Integer>> list=new ArrayList<>();
        if(nums==null||nums.length==0) return list;
        //因为是交换已经将位置调换了,则不需要subList来容纳了
        backtrace(list,nums,0);
        return list;
        
    }
    //递归部分
    public void backtrace(List<List<Integer>> list,int [] nums,int index){
        if(index==nums.length){
            //说明完成了一次组合
            List<Integer> subList=new ArrayList<>();
            for(int num:nums){
                subList.add(num);
            }
            list.add(subList);
            return ;
        }else{
            //递归继续查找
            for(int i=index;i<nums.length;i++){
                boolean flag=false;
                //查找前k-1个中是否存在相同的
                for(int j=index;j<i;j++){
                    if(nums[i]==nums[j]){
                        //说明存在相同,则不讲再次放置最前面
                        flag=true;
                        break;
                    }
                }
                
                //判断是否需要调换
                if(flag){
                    //存在相同,不重复再次放置
                    continue;
                }
                //主要步骤:index为当前需要处理的数下表,将第一个数固定和后面的每个数进行一次调换,便为一次不同的组合。
                
                int temp=nums[index];
                nums[index]=nums[i];
                nums[i]=temp;
                backtrace(list,nums,index+1);//对其后半部分进行递归排列
                //一个数的排列完了,则将其互换回来
                temp=nums[index];
                nums[index]=nums[i];
                nums[i]=temp;
            }
        }
    }
}

题目31:

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).

The replacement must be in-place and use only constant extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

分析:

class Solution {
    public void nextPermutation(int[] nums) {
        //按照字典序进行全排列
        //思路:分两步:第一步,从最后面查找第一个比其前面大的元素。nums[i]>nums[i-1]
        //之后在后半部分队列中后面开始查找到第一个比nums[i-1]大的元素,进行互换
        //第二步:将i-length之间的数组进行反转,这样最小的元素就在最前面了,满足字典序要求
        
        if(nums.length<1) return ;
        
        //查找第一个奇点
        int index=nums.length-1;
        for(;index>0;index--){
            if(nums[index]>nums[index-1]){
                break;
            }
        }
        
        //判断index是否为0
        if(index==0){
            //说明没有找到满足条件的奇点,那么对数组进行sort输出
            Arrays.sort(nums);
            return ;
        }
        
        for(int j=nums.length-1;j>=index;j--){
            if(nums[j]>nums[index-1]){
                //交换
                swap(nums,j,index-1);
                 //第二步:对index-length进行反转
                 reverse(nums,index,nums.length-1);
                break;
            }
        }
        
        
    }
    public void reverse(int [] nums,int start,int end){
        while(start<end){
            int temp=nums[start];
            nums[start]=nums[end];
            nums[end]=temp;
            
            start++;
            end--;
        }
    }
    public void swap(int [] nums,int i,int j){
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值