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

来源:互联网 发布:大数据在医疗领域应用 编辑:程序博客网 时间:2024/05/17 10:53


266Palindrome Permutation 52.6% Easy

46Permutations37.7% Medium
267Palindrome Permutation II 29.5% Medium
47Permutations II29.3% Medium
31Next Permutation 27.3%Medium
60Permutation 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|Pi<Pi+1}
         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<String> generatePalindromes(String s) {    int odd=0;    List<String> list=new ArrayList<>();    Map<Character,Integer> map=new HashMap<>();    for(int i=0;i<s.length();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<Character> 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;i<val/2;i++){            charlist.add(key);        }    }    getPumu(list,charlist,new StringBuilder(),new boolean[charlist.size()],str);    return list;}public void getPumu(List<String> list,List<Character> 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<charlist.size();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<n;i++){        list.add(i+1);        multiply*=i+1;    }    String res="";    for(int i=0;i<n;i++){        multiply=multiply/(n-i);        int index=multiply/k;        res+=list.get(index);        list.remove(index);        k=multiply%k;    }    return res;}
   

1 0
原创粉丝点击