https://leetcode.com/problems/permutation-sequence/
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):
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note: Given n will be between 1 and 9 inclusive.
解题思路:
开始把这题当成 Permutations ,直接dfs出所有组排列,然后去第k个,按照本题的意思,是一定会超时的,反正代码也写一下吧。
public class Solution { public String getPermutation(int n, int k) { List<String> permutationList = new ArrayList<String>(); int[] visited = new int[n]; dfs(permutationList, n, new StringBuffer(), 0, visited); return permutationList.get(k - 1); } public void dfs(List<String> permutationList, int n, StringBuffer current, int step, int[] visited){ if(current.length() == n){ permutationList.add(current.toString()); return; } for(int i = 0; i < n; i++){ if(visited[i] == 1){ continue; } current.append(i + 1); visited[i] = 1; dfs(permutationList, n, current, step + 1, visited); current.deleteCharAt(current.length() - 1); visited[i] = 0; } } }
然后再一想,对于上面的例子,比如k=5,那么前4次完全可以不算,因为第一位肯定是3了。为什么?因为以每个数字开头的组合,都一定有(n-1)!个。所以我们只要看这第k个数字在第m组的第n个就可以了。
这里m=(k - 1) / (n - 1)! + 1,n=(k-1) % (n - 1)! + 1。所以要求的第k个数字,一定是mxxxxxx的形式,而且在这xxxxxx的组合中,是第n个。
代码写出来,仍然超时。
public class Solution { public String getPermutation(int n, int k) { List<String> permutationList = new ArrayList<String>(); int[] visited = new int[n]; int factorial = fac(n - 1); int base = (k - 1) / factorial; int kth = (k - 1) % factorial + 1; visited[base] = 1; dfs(permutationList, n, new StringBuffer(String.valueOf(base + 1)), kth, visited, base); return permutationList.get(kth - 1); } public void dfs(List<String> permutationList, int n, StringBuffer current, int kth, int[] visited, int base){ if(current.length() == n){ permutationList.add(current.toString()); return; } if(permutationList.size() == kth){ return; } for(int i = 0; i < n; i++){ if(visited[i] == 1){ continue; } current.append(i + 1); visited[i] = 1; dfs(permutationList, n, current, kth + 1, visited, base); current.deleteCharAt(current.length() - 1); visited[i] = 0; } } public int fac(int n){ int result = 1; for(int i = 1; i <= n; i++){ result *= i; } return result; } }
这时才想到,上面的思路不正是一个递归思路吗?或者迭代,这里都比较简单。我们对于第一位数字可以如此确定,后面所有位的数字也都可以这么确定了。
不能忘记的是,还是要有个去重的问题,当前第k个数字往前,如果已经选择过x个了,那么当前数字就是k+x。
public class Solution { public String getPermutation(int n, int k) { int[] visited = new int[n]; return generate(n, k, visited); } public String generate(int n, int k, int[] visited){ if(n == 0){ return ""; } int factorial = fac(n - 1); int base = (k - 1) / factorial; int kth = (k - 1) % factorial + 1; //除去前面已经选择过的数字 int temp = 0; for(int i = 0 ; i < visited.length; i++){ if(visited[i] == 1){ base++; } if(i == base){ break; } } visited[base] = 1; return String.valueOf(base + 1) + generate(n - 1, kth, visited); } public int fac(int n){ int result = 1; for(int i = 1; i <= n; i++){ result *= i; } return result; } }
update 2015/06/04:
二刷,写了个迭代的。
public class Solution { public String getPermutation(int n, int k) { String res = ""; int[] visited = new int[n + 1]; for(int i = 0; i < n; i++) { //从左往右迭代到第i位 int bucketSize = fac(n - 1 - i); //第i位的桶大小 int digit = (k - 1) / bucketSize + 1; //应该在第几个桶,这一位就是digit,但是要去掉已经选中的数字 k = k - bucketSize * (digit - 1); //方法1,剩下里面在第k个 k = (k - 1) % bucketSize + 1; //方法2,剩下里面在第k个 for(int j = 1; j <= digit; j++){ if(visited[j] == 1){ digit++; } } visited[digit] = 1; res = res + String.valueOf(digit); } return res; } public int fac(int n) { int res = 1; for(int i = 1; i <= n; i++) { res *= i; } return res; } }