问题
LeetCode 400题
集合[1,2,3……,n]共包含n!不同的排列,把他们从小到大排序可以得到:
"123"
"132"
"213"
"231"
"312"
"321"
现在给定 n n n和 k k k,找到第 k k k个全排列。
解析
回溯是解决全排列问题的最最最通用的解法,当然,同样适用于该问题。但是用回溯解决该问题复杂度实在太高,而且这里不需要求得所有的全排列,只需要求第
k
k
k个全排列,所以我们可以转变一下思路。我们先看一下第
k
k
k个全排列有没有什么规律。
当n=3时,前两个数是1开头,中间两个数是2开头,最后面两个数是3开头。那么当
1
<
=
k
<
=
2
1<=k<=2
1<=k<=2时,第
k
k
k个全排列的第一位是1;依次类推得到k的取值范围和第一位对应的关系。
我们能不能按照这样的规律继续深入下去确定第二位,第三位? 答案是肯定的。假设我们确定了第一位数是1,那么第二位数在2,3中选,如何选?同样的,把k的范围进一步缩小,缩小到是
1
<
=
k
<
=
1
1<=k<=1
1<=k<=1或者
2
<
=
k
<
=
2
2<=k<=2
2<=k<=2。问题来了,如何在代码中体现这种范围的缩小过程呢?我总不能写一大堆判定语句吧?下图详细地给出了如何判断第
i
i
i位的数字:
剩下的就是取商,判断位置,然后更新更准确的位置,然后取商…
Java代码
public String getPermutation(int n, int k) {
StringBuilder sb = new StringBuilder();
List<Integer> list = new LinkedList();
int sum = 1;
int i=1;
for(i=1;i<=n;i++){
sum*=i;
list.add(i);
}
i=i-1;
while(i>0){
sum = sum/i;
int temp = k/sum;
if(temp*sum==k) {
sb.append(list.get(temp-1));
list.remove(temp-1);
temp = temp-1;
}
else {
sb.append(list.get(temp));
list.remove(temp);
}
k = k - temp*sum;
i--;
}
return sb.toString();
}