leetcode 关于全排列题目的简单总结


266
Palindrome Permutation  52.6%   Easy
46 Permutations 37.7%   Medium
267 Palindrome Permutation II  29.5%   Medium
47 Permutations II 29.3%   Medium
31 Next Permutation  27.3% Medium
60 Permutation Sequence  26.0%   Medium
leetcode上一共六道涉及到全排列的问题,这里简单的总结一下:

第一 如何求一个字符串的全排列

求全排列比较常见的是两种方法,一种是递归,一种是字典序的方法,递归思想是这样的,我们试想一个简单的例子,123的全排列:
123

132

213

231

312

321

我们可以理解为分别将长度为n的数组的每一个数与数组的第一个数进行交换,然后对后面的n-1子数组进行全排列,这样得到的代码也比较简洁:

public void arrange (String[] str, int st, int len)
{
    if (st == len - 1)
    {
        for (int i = 0; i < len; i ++)
        {
            System.out.print(str[i]+ "  ");
        }
        System.out.println();
    }
    else
    {
        for (int i = st; i < len; i ++)
        {
            swap(str, st, i);
            arrange(str, st + 1, len);
            swap(str, st, i);
        }
    }
}
字典序是指将一个字符串以字典序升序的方式排列,再如上例的123,当按照一定的交换规则变成321,即不存在升序的时候表示排列完成,字典序的全排列生成是有严格顺序的,c++中有next_permutation函数来生成一个排列的下一个字典序排列,字典序的生成规则如下:

一般而言,设P是[1,n]的一个全排列。
      P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn
    find:  j=max{i|Pi1}
         k=max{i|Pi>Pj}       1, 对换Pj,Pk,
      2, 将Pj
+1…Pk-1PjPk+1…Pn翻转
P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个
【例】 如何得到346987521的下一个
    1,从尾部往前找第一个P(i-1) < P(i)的位置
            3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
        最终找到6是第一个变小的数字,记录下6的位置i-1
    2,从i位置往后找到最后一个大于6的数
            3 4 6 -> 9 -> 8 -> 7 5 2 1
        最终找到7的位置,记录位置为m
    3,交换位置i-1和m的值
            3 4 7 9 8 6 5 2 1
    4,倒序i位置后的所有数据
            3 4 7 1 2 5 6 8 9
    则347125689为346987521的下一个排列
next_permutation java代码:

public void nextPermutation(int[] num) {
    //1.找到最后一个升序位置pos  
    int pos = -1;
    for (int i = num.length - 1; i > 0; i--) {
        if (num[i] > num[i - 1]) {
            pos = i - 1;
            break;
        }
    }

    //2.如果不存在升序,即这个数是最大的,那么反排这个数组  
    if (pos < 0) {
        reverse(num, 0, num.length - 1);
        return;
    }

    //3.存在升序,那么找到pos之后最后一个比它大的位置  
    for (int i = num.length - 1; i > pos; i--) {
        if (num[i] > num[pos]) {
            int tmp = num[i];
            num[i] = num[pos];
            num[pos] = tmp;
            break;
        }
    }

    //4.反排pos之后的数  
    reverse(num, pos + 1, num.length - 1);
}

public void reverse(int[] num, int begin, int end) {
    int l = begin, r = end;
    while (l < r) {
        int tmp = num[l];
        num[l] = num[r];
        num[r] = tmp;
        l++;
        r--;
    }
}
代码是leetcode next permutation的ac代码,按照要求当字符串是完全降序的时候倒序字符串,整个过程就是next_permutation的思想。

下面来看一下palindrome permutationII和permutation sequence两道题:

Given a string s, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form.

For example:

Given s = "aabb", return ["abba", "baab"].

Given s = "abc", return [].

Hint:

  1. If a palindromic permutation exists, we just need to generate the first half of the string.
  2. To generate all distinct permutations of a (half of) string, use a similar approach from: Permutations II or Next Permutation.
这道题的提示比较明确

第一,生成回文全排列,我们只需要生成前半部分,然后倒转生成后半部分即可;

第二,生成全排列的方法,这个生成方法有很多,记得需要考虑重复元素的问题;

需要注意的地方有:
1)回文只允许至多一个奇数次的字符,若多于一个必定无法构成回文。

2)奇数长度和偶数长度如何处理

这里引用https://discuss.leetcode.com/topic/22214/ac-java-solution-with-explanation的代码,清晰明了:

public List generatePalindromes(String s) {
    int odd=0;
    List list=new ArrayList<>();
    Map,Integer> map=new HashMap<>();
    for(int i=0;i;i++){
        char temp=s.charAt(i);
        map.put(temp,map.containsKey(temp)?map.get(temp)+1:1);
        odd+=map.get(temp)%2==1?1:-1;
    }
    String str="";
    List charlist=new ArrayList<>();
    if(odd>1) return list;
    for(Character key:map.keySet()){
        if(map.get(key)%2==1) str+=key;
        int val=map.get(key);
        for(int i=0;i2;i++){
            charlist.add(key);
        }
    }
    getPumu(list,charlist,new StringBuilder(),new boolean[charlist.size()],str);
    return list;

}
public void getPumu(List list,List charlist,StringBuilder sb,boolean[] used,String str){
    if(sb.length()==charlist.size()){
        list.add(sb.toString()+str+sb.reverse().toString());
        sb.reverse();
        return;
    }
    for(int i=0;i;i++){
        if(i>0&&charlist.get(i)==charlist.get(i-1)&&!used[i-1]) continue;
        if(!used[i]){
            used[i]=true;
            sb.append(charlist.get(i));
            getPumu(list,charlist,sb,used,str);
            used[i]=false;
            sb.deleteCharAt(sb.length()-1);
        }
    }

}

 

The set [1,2,3,…,n] contains a total of n! 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.

当然可以用next_pumutaion多次调用,但是细心观察显然是有数学规律的,显然1-n的全排列有n!种排列,而以1、2、3......n为开头的排列各有(n-1)!种排列,那么所求p=k/(n-1)!的nums[p]即为该排列的第一个数字,依次类推可以求出该排列的所有数字,代码:

public String getPermutation(int n, int k) {
    List list=new ArrayList<>();
    int multiply=1;
    for(int i=0;i;i++){
        list.add(i+1);
        multiply*=i+1;
    }
    String res="";

    for(int i=0;i;i++){
        multiply=multiply/(n-i);
        int index=multiply/k;
        res+=list.get(index);
        list.remove(index);
        k=multiply%k;
    }
    return res;

}
   

你可能感兴趣的:(leetcode 关于全排列题目的简单总结)