LeetCode60-第k个排列

LeetCode60-第k个排列

给出集合 [ 1 , 2 , 3 , … , n ] [1,2,3,…,n] [1,2,3,,n],其所有元素共有 n! 种排列。

按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:

"123"
"132"
"213"
"231"
"312"
"321"

给定 n 和 k,返回第 k 个排列。

说明:

给定 n 的范围是[1, 9]。
给定 k 的范围是[1, n!]。

示例 1:

输入: n = 3, k = 3
输出: "213"

示例 2:

输入: n = 4, k = 9
输出: "2314"

一、思路

(一)字典序

我们知道,如何求解下一个字典序:

(1)从右往左找出第一个:顺序对

j = m a x ( i ∣ n u m [ i ] ) &lt; n u m [ i + 1 ] j={max(i|num[i])&lt;num[i+1]} j=max(inum[i])<num[i+1]

(2)从 n u m [ j ] num[j] num[j]的位置开始,往右找到第一个大于 n u m [ j ] num[j] num[j]的数: n u m [ k ] num[k] num[k]

k = m i n ( i ∣ n u m [ i ] &gt; n u m [ j ] 并 且 i &gt; j ) k={min(i|num[i]&gt;num[j]并且i&gt;j)} k=min(inum[i]>num[j]i>j)

(3)交换 n u m [ k ] num[k] num[k] n u m [ j ] num[j] num[j]的位置
(4)将 j j j之后的数组进行翻转:

原来是: n u m [ j + 1 ] . . . . n u m [ k ] . . . . n u m [ n ] num[j+1]....num[k]....num[n] num[j+1]....num[k]....num[n]
翻转后: n u m [ n ] n u m [ n − 1 ] . . . . n u m [ k ] . . . . n u m [ j + 1 ] num[n]num[n-1]....num[k]....num[j+1] num[n]num[n1]....num[k]....num[j+1]

(5)组合起来即得到下一个排序

这个方法显然是不适用于这道题,我们知道k最大为n!,此时这个算法的时间复杂度将会超出你的想象

(二)算数技巧

这道题本质上是一个算数的问题。

我们知道以 i i i 开头的序列有:(n-1)! 个
并且以 ( i + 1 ) (i+1) i+1开头的序列必然会大于以 i i i 开头的序列

所以我们知道了:
k k k 如果属于 [ 1 , ( n − 1 ) ! ] [1,(n-1)!] [1,(n1)!] ,那么它的排列的第一个数字一定是:1
k k k 如果属于 [ ( n − 1 ) ! + 1 , 2 ∗ ( n − 1 ) ! ] [(n-1)!+1,2*(n-1)!] [(n1)!+1,2(n1)!] ,那么它的排列的第一个数字一定是:2

因此,我们通过这样的方法,来缩小排列的范围

在确定了第一个范围之后,第二个范围也能用同样的方法确定下来,一直到第n个数确定下来

从上面的描述可以知道,这是一个递归求解的方案。

C++代码

class Solution {
public:
	string s;
	string getPermutation(int n, int k) {
		string sum = "123456789";
		string nums = sum.substr(0, n);
		recursion(k, n, nums);
		return s;
	}

	void recursion(int k, int n, string nums) {
		if (n == 0)
			return;
		int fact = factorial(n - 1);
		int m = (k - 1) / fact;
		n--;
		s.push_back(nums[m]);
		nums.erase(m, 1);
		k -= m * fact;
		recursion(k, n, nums);
	}

	int factorial(int n) {
		int s = 1;
		for (int i = 1; i <= n; i++)
			s *= i;
		return s;
	}
};

执行效率:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值