返回第k个排列

给定数字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);
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值