欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数组的全排列

程序员文章站 2024-03-24 12:32:58
...

Leetcode 46. Permutations 数组的全排列

· TotalAccepted: 160245
· TotalSubmissions: 378230
· Difficulty: Medium
· Contributor: LeetCode
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]
]
法一:使用回溯法,先固定一个字符不变,然后全排列后面的字符;使用后面的一个字符替换固定的字符,再求一次后面的全排列。
比如123,先固定1,求23的全排列(固定2,求3的全排列;2和3交换,固定3,求2的全排列) ;1和2交换,固定2,求13的全排列(固定1,求3的全排列…)…

public class Solution {
    public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> result = new ArrayList<List<Integer>>();
    perm(result,nums,0,nums.length-1);
    return result;
}
public static void perm(List<List<Integer>> result, int[] nums, int start, int end){
    if(start==end){
        Integer[] ele = new Integer[nums.length];
        for(int i=0; i<nums.length; i++){
            ele[i] = nums[i];
        }
        result.add(Arrays.asList(ele));
    }
    else{
        for(int i=start; i<=end; i++){
            int temp = nums[start];
            nums[start] = nums[i];
            nums[i] = temp;
            perm(result, nums,start+1,end);

            temp = nums[start];
            nums[start] = nums[i];
            nums[i] = temp;
        }
    }
}
}

按照法1的思路还有另外一种写法(效率稍微差一点,但是可以对于下一题代码的理解有帮助作用。)

public class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        List<Integer> list = new ArrayList<Integer>();
        boolean[] used = new boolean[nums.length];
        perm(result,nums,list,used);
        return result;
    }
    public void perm(List<List<Integer>> res,int[] nums,List<Integer> list,boolean[] used){
        if(list.size() == nums.length){
            List<Integer> temp = new ArrayList<Integer>(list);
            res.add(temp);
        }
        else{
            for(int i = 0; i < nums.length; i++){
                if(used[i]) continue;
                used[i] = true;
                list.add(nums[i]);
                perm(res,nums,list,used);
                used[i] = false;
                list.remove(list.size()-1);
            }
        }
    }
}

法2:和法1类似,但是我感觉比第一种方法更好理解一点,可以叫他插入法,也是使用回溯的思想。
还拿123来举列子,先插入1,然后2的位置可以在1之前也可以在1之后:把2插入在1之前(21),然后3的插入有3中位置,先插入到最前面得到321,然后移除3,插入到中间231,移除3,插入到最后213,移除3之后回溯到插入2的步骤,移除2,把2插入到1的后面就变成了12,再插入3(也是3种情况312,132,123)…

public class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        List<Integer> list = new ArrayList<Integer>();
        perm(result,nums,0,list);
        return result;
    }
    public void perm(List<List<Integer>> res,int[] nums,int end,List<Integer> list){
        if(end == nums.length){
            List<Integer> temp = new ArrayList<Integer>(list);
            res.add(temp);
        }
        else{
            for(int i = 0; i <= end; i++){
                list.add(i,nums[end]);
                perm(res,nums,end+1,list);
                list.remove(i);
            }
        }
    }
}

Leetcode 47. Permutations II 数组的全排列2
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,1,2,就是已经固定过一个1,求12的全排列了,那么下一个1就不进行固定求全排列的流程了,直接continue,固定下一个字符。理解者有困难的话可以看上一道题法1的第二个版本,这个是在那个版本的基础上修改的。

public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        List<Integer> list = new ArrayList<Integer>();
        boolean[] used = new boolean[nums.length]; 
        Arrays.sort(nums);
        perm(nums,list,res,used);
        return res;
    }
    public void perm(int[] nums,List<Integer> list,List<List<Integer>> res,boolean[] used){
        if(list.size() == nums.length){
            res.add(new ArrayList<Integer>(list));
        }
        else{
            for(int i = 0; i < nums.length; i++){
                //已经被使用过了
                if(used[i]) continue;
                //和前一个一样,并且前一个没有被使用,前一个现在没有被使用
                //其实就是代表着已经在上一轮是用过了,
                //那么与他相同的字符这一轮在使用就和上一轮得到的效果应该完全相同,所以直接continue
                if(i > 0 && nums[i-1] == nums[i] && !used[i-1]) continue;
                used[i] = true;
                list.add(nums[i]);
                perm(nums,list,res,used);
                used[i] = false;
                list.remove(list.size() - 1);
            }
        }
    }
}