排列组合程序

本文探讨了排列组合的相关算法,包括Next Permutation、Permutation Sequence、Combinations、Subsets及其变种问题。通过实例解析并提供了源码,阐述了如何在给定条件下找出下一个排列、指定位置的排列序列、组合以及不重复和重复元素的子集。同时,讨论了去重情况下独特排列的生成方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Next Permutation

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, do not allocate 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,31,3,2
3,2,11,2,3

1,1,51,5,1

交换+排序

提供两个易错的例子:

1,4,3,2  -> 2,1,3,4

2,3,1  -> 3,2,1

代码如下:

public class Solution { 
        public static void nextPermutation(int[] nums){ 
        if(nums == null || nums.length < 2) return; 
        int pos = nums.length-1,i = pos-1; 
        while(i >= 0 && nums[i] >= nums[i+1]) i--; 
        if(i<0) {Arrays.sort(nums); return;} 
         
        for(;pos>i;pos--) if(nums[pos]>nums[i]) break; 
        swap(nums,i,pos); 
        Arrays.sort(nums,i+1,nums.length); 
    } 
    public static void swap(int[] nums,int i,int j){ 
        int tmp = nums[i]; 
        nums[i] = nums[j]; 
        nums[j] = tmp; 
    } 
}

2.Permutation Sequence


The set [1,2,3,…,n] contains a total ofn! unique permutations.

By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):

  1. "123"
  2. "132"
  3. "213"
  4. "231"
  5. "312"
  6. "321"

Given n and k, return the kth permutation sequence.

Note: Given n will be between 1 and 9 inclusive.

最容易想到的一种方法就是根据上一题中的nextPermutation依次计算每一个序列,但是这样会超时。

其实是可以直接计算的,我们可以把首位数字看成索引,比如上面的1,2,3就是索引,每个索引这种的排列数为2!那么根据k就能计算索引,对所有的位重复这个过程就能计算出结果

代码如下:

public class Solution {
    public String getPermutation(int n, int k) {
        StringBuilder res = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        for(int i = 1;i <= n;i++) sb.append(i);
        int count = k,index = 0,fac = fac(n);
        for(int i = n;i >= 1;i--){
            fac /= i;
            index = (count-1)/fac;
            res.append(sb.charAt(index));
            sb.deleteCharAt(index);
            count -= index*fac;
        }
        return res.toString();
    }
    
    public int fac(int n){
        int res = 1;
        for(int i = 1;i <= n;i++) res *= i;
        return res;
    }
}

3.Combinations

Given two integers n and k, return all possible combinations ofk numbers out of 1 ...n.

For example,
If n = 4 and k = 2, a solution is:

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

可以用递归来做,遍历每一个元素,每个元素可选可不选,判断已经选择的元素的数量,如果为k就加入最终结果集合中。代码如下:

public class Solution {
    public List<List<Integer>> combine(int n, int k) {
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        helper(n,k,1,tmp,res);
        return res;
    }
    public void helper(int n,int k,int from,ArrayList<Integer> tmp,List<List<Integer>> res){
        if(tmp.size() == k){
            res.add(new ArrayList(tmp));
        }
        for(int i = from; i <= n; i++){
            tmp.add(i);
            helper(n,k,i+1,tmp,res);
            tmp.remove(tmp.size() - 1);
        }
    }
}

4.Subsets

Given a set of distinct integers,nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,3], a solution is:

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

求一个集合的子集,这个问题有多种解法。比如第一种方法,利用第3题的方法,一个集合的子集就是从中取出0个元素、1个元素...n个元素的全部情况,但是这种方法效率太低;第二种方法,还是采用第3题的思路,遍历每个元素,它可以加入或者不加入,只不过这里不再是当元素数到k时才加入结果集,而是每次都要加入。代码如下:

第一种方法:

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        res.add(new ArrayList<Integer>());
        for(int i=1;i<=nums.length;i++)
            helper(nums,i,0,tmp,res);
        return res;
    }
    public void helper(int[] nums,int k,int from,ArrayList<Integer> tmp,List<List<Integer>> res){
        int len = nums.length;
        if(tmp.size() == k) res.add(new ArrayList<Integer>(tmp));
        for(int i = from; i < len; i++){
            tmp.add(nums[i]);
            helper(nums,k,i+1,tmp,res);
            tmp.remove(tmp.size()-1);
        }
    }
}

第二种方法:

public class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        helper(nums,0,tmp,res);
        return res;
    }
    public void helper(int[] nums,int from,ArrayList<Integer> tmp,List<List<Integer>> res){
        int len = nums.length;
        res.add(new ArrayList<Integer>(tmp));
        for(int i = from; i < len; i++){
            tmp.add(nums[i]);
            helper(nums,i+1,tmp,res);
            tmp.remove(tmp.size()-1);
        }
    }
}

5.Subsets II

Given a collection of integers that might containduplicates,nums, return all possible subsets.

Note: The solution set must not contain duplicate subsets.

For example,
If nums = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
涉及到去重问题,去重的思路是这样的,比如有一个数组【abC】,其中C是一串数字,我们在遍历到a时,会对后面的bC以及C进行处理;当我们遍历到b时,会对后面的C进行递归处理,那么如果a和b相同,就会出现重复。

代码如下:

public class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        Arrays.sort(nums);
        helper(nums,0,tmp,res);
        return res;
    }
    
    public void helper(int[] nums,int pos,List<Integer> tmp, List<List<Integer>>res){
        res.add(new ArrayList<Integer>(tmp));
        for(int i = pos;i < nums.length;i++){
            if(i == pos || nums[i] != nums[i-1]){
                tmp.add(nums[i]);
                helper(nums,i+1,tmp,res);
                tmp.remove(tmp.size()-1);
            }
        }
    }
}

6.Permutations

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]
]

两种方法,第一种方法比较容易理解,因为每个位置上每个数都有可能出现,递归所有可能即可;第二种方法,采用交换元素的方法。

这里给出第二种的源码:

public class Solution {
    public List<List<Integer>> permute(int[] nums) {
        if(nums == null || nums.length == 0) return null;
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        helper(nums,0,res);
        return res;
    }
    
    public void helper(int[] nums,int pos,List<List<Integer>> res){
        if(pos == nums.length){
            ArrayList<Integer>tmp = new ArrayList<Integer>();
            for(int n:nums) tmp.add(n);
            res.add(tmp);
            return;
        }
        Arrays.sort(nums,pos,nums.length);//加上这句之后,得到的序列就是递增的
        for(int i = pos;i < nums.length;i++){
            swap(nums,pos,i);
            helper(nums,pos+1,res);
            swap(nums,pos,i);
        }
    }
    
    public void swap(int[] nums,int i,int j){
        if(i == j) return;
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

7.Permutations II

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]
]

涉及到去重问题

代码如下:

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        if(nums == null || nums.length == 0) return null;
        ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
        Arrays.sort(nums);
        helper(nums,0,res);
        return res;
    }
    public void helper(int[] nums,int pos,ArrayList<List<Integer>> res){
        if(pos == nums.length-1){
            ArrayList<Integer> tmp = new ArrayList<Integer>();
            for(int n:nums) tmp.add(n);
            res.add(tmp);
            return;
        }
        Arrays.sort(nums,pos,nums.length);
        for(int i = pos;i < nums.length;i++){
            if(i != pos && nums[i] == nums[i-1]) continue;
            swap(nums,pos,i);
            helper(nums,pos+1,res);
            swap(nums,pos,i);
        }
    }
    public void swap(int[] nums,int i,int j){
        if(i == j) return;
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值