leetcode60 第k个排列——康托变换与逆康托变换
康托变换:
给定从1到n的n个自然数,将这些自然数的全排列按字典序从小到大排列,如何写出第k大的那个排列?
自然,最开始的思路是直接按照字典序写出所有的排列,返回其第k个,但是这样时间和空间复杂度应该都会很大。
这个问题是一个比较封闭问题,能不能直接找到所有的排列及其顺序的一个一一映射公式呢?康托排列就解决了这一问题。
https://www.zhihu.com/search?type=content&q=康托展开%20老王
对于1到n这n个从小到大排列的数字序列,我们每次选取其中的一个数字,同时记录下这个被选取的数字在序列中的位置(从0开始),并将其拿出序列,剩下的元素顺序递补。将序列中所有的数字都拿出来后,我们就得到了一个位置序列,这个序列称为次序序列。次序序列和所有排列是按照字典序一一对应的。
接下来只需将次序序列与自然数再次进行按照顺序的一一对应就可以了。可以看到,次序序列中从低位到高位,数字的最大取值值是从0到n-1,因此可以用一个计算机中多维数组的储存方式来进行分析。想象一个维度从低到高分别是1,…,n的多维数组,我们想知道一个给定下标的元素究竟位于线性排列存储单元的内存中的什么位置,那么对于该元素的每个维度而言,假设该维度是第k维,每个这个维度的一个存储单元包含的存储块就刚好是(k-1)!个,按照从高维到低维的顺序把所有维度的存储块个数加起来,就得到了该元素的物理位置。这刚好就是次序序列与自然数的一一对应。
设次序序列为AnAn-1An-2…A1,其中Ak的取值范围为0到Ak-1,则对应的自然数应该是:An*(n-1)! + An-1*(n-2)! + … + A1*0!。
同样的道理,给定自然数,也可以得到次序序列,接着还原出排列。
代码:
class Solution {
int[] sq = new int[10];
public String getPermutation(int n, int k) {
k -= 1;
for(int i = n; i >= 1;i--){
int fac = 1;
for(int j = 1;j <= i-1;j++){
fac *= j;
}
sq[n-i] = k / fac;
k = k % fac;
}
List<Integer> tmp = new ArrayList<>();
for(int i = 1;i <= n;i++){
tmp.add(i);
}
String res = "";
for(int i = 0;i < n;i++){
res += tmp.get(sq[i]);
tmp.remove(sq[i]);
}
return res;
}
}
2ms 35.9MB