给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。
示例 1:
输入:n = 3, k = 3
输出:“213”
示例 2:
输入:n = 4, k = 9
输出:“2314”
最容易的当然还是暴力递归,直到找到目标序列结束。但是没有用到题目给定的规律。
所以要找规律。这道题如果直接去想规律的话。不太好想。可以先换一种角度。假设现在已经给定了排列和n。求k值。
比如:给定 “213” 和 3。那么如何求k呢?
k = 1 * (n - 1)! + 0 * (n - 2)! + 0 * (n - 3)! + 1
关于这个式子的解释:对第一个数2来说,比2小的数有1,那么以1为第一个数共有 (n - 1)!个。同理,对于第二个数1来说,目前尚未用到的且比它小的数有0个。所以是 0 * …。直到最后把213之前的序列数量都算出来求和,最后 + 1。即为k的值。
回到本题,现在给定k值和n,求序列。也就是上述过程的逆过程。只需不断的求商求余就能确定序列每一个位置的值。
class Solution {
public String getPermutation(int n, int k) {
// 下标0表示数字1是否被用过
boolean[] isUse = new boolean[n];
StringBuilder sb = new StringBuilder();
int[] factorial = new int[n];
factorial[0] = 1;
for (int i = 1; i < n; ++i) {
factorial[i] = factorial[i - 1] * i;
}
--k;
for (int i = n - 1; i > -1; --i) {
int a = k / factorial[i] + 1;
int tag = 0;
for (int j = 0; j < n; ++j) {
tag = isUse[j] ? tag : tag + 1;
if (tag == a) {
sb.append(j + 1);
isUse[j] = true;
break;
}
}
k %= factorial[i];
}
return sb.toString();
}
}