给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"
答案:
1public String getPermutation(int n, int k) {
2 List<Integer> num = new LinkedList<Integer>();
3 for (int i = 1; i <= n; i++)
4 num.add(i);
5 int[] fact = new int[n];
6 fact[0] = 1;
7 for (int i = 1; i < n; i++)
8 fact[i] = i * fact[i - 1];
9 k = k - 1;
10 StringBuilder sb = new StringBuilder();
11 for (int i = n; i > 0; i--) {
12 int ind = k / fact[i - 1];
13 k = k % fact[i - 1];
14 sb.append(num.get(ind));
15 num.remove(ind);
16 }
17 return sb.toString();
18}
解析:
上面数组fact的值也就是阶乘的值,集合n中有n的阶乘种组合。比如当n=4,k=14的时候,
1(2,3,4),1+(2,3,4)的全排列,有6种,
2(1,3,4),同理都有6种组合,
3(1,2,4),也有6种组合,但是前面的两种已经到12了,所以这里只需要2种组合就行了,也就是31+(2,4)的两种排列组合,正好加起来是14,其中第13个是3124,第14个是3142,所以结果是3142。
看了上面的例子,我们再来分析这道题,其实这道题应该算是一道数学题。
因为最终结果是k位,我们从第一位开始找,第一位也就是index = k / (n-1)!,(n-1)!也就是去掉最前面的那位之后,后面的几种排列组合(参照上面的分析),其中(n-1)!是fact(n-1)的阶乘,找到第一位之后,k值就要更新,变为k = k%(n-1)!,这个也很好理解,同理然后再找第二位,也就是index = k / (n-2)!……如果对排列组合不是很熟悉的话,理解起来可能会有些费劲。下面再来看一种写法。
1public String getPermutation(int n, int k) {
2 k--;
3 int fact = 1;
4 char[] result = new char[n];
5 result[n - 1] = '1';
6 for (int i = 2; i <= n; i++) {
7 fact *= i;
8 result[n - i] = (char) (k % fact * i / fact + '1');
9 for (int j = n - i + 1; j < n; j++) {
10 result[j] += result[j] >= result[n - i] ? 1 : 0;
11 }
12 k -= k % fact;
13 }
14 return new String(result);
15}
这个解法稍微有点烧脑了,第一个方法是从第一位开始往后找,这个方法是从最后一位往前找,这个k个长度的字符串可以用下面这个公式来表示
(n-1)!*A[0] + (n-2)!*A[1] + ... + 2!*A[n-3] + 1!A[n-2] + 0!A[n-1]并且0 <= A[i] < n-i
这个公式很容易推导,也很好理解,比如说第一位也就是上面说的index = k / (n-1)!,所以反过来不就是k= index *(n-1)!吗,同理,后面的也都一样……
很明显0!A[n-1]是等于0的,因为0<=A[n-1]<1,所以A[n-1]只能是0,所以我们可以令最后一个元素result[n - 1] = '1';从后往前推。(注意n是从1开始的,而我们的数组下标是从0开始的)