给定数字n,易知n一共有n!个排列组合,现在要返回第k个排列(list是以从小到大的顺序排列的)。
比如:n=3,k=4,全排列为[123, 132, 213, 231, 312, 321],返回"231"。
一个比较容易想到的做法就是:先得到全排列List,然后返回List[k-1]
使用Python中的itertools,很容易就能实现:
def permulation(self, n, k):
nums = [i for i in range(1,n+1)]
List = list(itertools.permutations(nums,n))
return List[k-1]
但是这种做法时间复杂度和空间复杂度极高,当n很大的时候,这种算法基本不可取!
所以这里介绍一种时间复杂度为O(n)的做法:
1、需要一个list存放1到n的所有数:[1,2,3,...,n]
2、对于每一位上的数字:num = k/(n-1)! 向上取整。
比如:n = 4, k = 7,那么num = 7 / 3的阶乘 = 7/6,向上取整为2,所以第一个数字应该取list中的2。
因为按顺序排列的话,它的排列规则肯定是 [6个1____,6个2____,6个3____,6个4____]。
3、然后list中需要把2位置上的数删去 (因为1个数只能被选一次)
4、之后 k 要减去 (num-1)*(n-1)!。它的作用是(以n=3,k=6为例):
123,132,213,231,312,321 在第一次循环后,已经确定了第一个数字是3,所以在判断第二个数字时,其实是在[312, 321]中判断,所以第二次的k要代表在[312, 321]中的位置,所以需要去掉之前的[123, 132, 213, 231];之前这些数的个数即为(num-1)*(n-1)!。这时的k=2,和之前的6一样,都代表321。
以此类推……
总体步骤如下(以n=3,k=5为例):
1、[123, 132, 213, 231, 312, 321],n=3,k=5,list=[1,2,3],
num = Ceil( k/(n-1)! ) = Ceil( 5/2 ) = 3 ( Ceil代表向上取整 ) ,list中取“3”
n = n - 1 = 2
k = k - (num-1)*(n-1)! = 5 - 2*2 = 1
2、[312, 321],n=2,k=1,list=[1,2]
num = Ceil( k/(n-1)! ) = Ceil( 1/1 ) = 1,list中取“1”
n = n - 1 = 1
k = k - (num-1)*(n-1)! = 1
3、最后直接取list中的最后一个元素“2”,组合成“312”
这样就不用列出所有全排列了!
具体代码(Java):
public class Solution {
//求阶乘
public static int Factorial(int n) {
while(n > 1) {
return n*Factorial(n-1);
}
return 1;
}
public String permutation(int n, int k) {
String result = "";
Vector<Integer> v = new Vector<Integer>();
for(int i=1;i<=n;i++) v.add(i);
int num = 0, f = 0;
for(int i=1;i<n;i++) {
k -= (num-1)*f;
f = Solution.Factorial(n-i);
num = (int)Math.ceil((double)k/f);
result += String.valueOf(v.get(num-1));
v.remove(num-1);
}
return result+v.get(0);
}
}