文章目录
leetcode60:60. 第k个排列
题目描述
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3
时, 所有排列如下:
-
“123”
-
“132”
-
“213”
-
“231”
-
“312”
-
“321”
给定
n
和k
,返回第k
个排列。说明:
-
给定
n
的范围是 [1, 9]。 -
给定
k
的范围是[1, n!]
-
Example
输入:n = 3, k = 3
输出:"213"
solution idea
next_permutation 内置字符串函数
``C++ STL中提供了
std::next_permutation与
std::prev_permutation可以获取数字或者是字符的全排列,其中
std::next_permutation提供升序、
std::prev_permutation`提供降序。
class Solution {
/*
** next_permutation 内置字符串函数
*/
public:
string getPermutation(int n, int k) {
string s=string("123456789").substr(0,n);
for(int i=1; i<k; i++) next_permutation(s.begin(),s.end());
return s;
}
};
根据规律直接查找第k个排列
考虑到我们的目标仅仅是找到第k个排列,有没有办法不用列举中间的排列,直接根据输入信息和排列规律直接找到第k个排列?在n个数字的排列中,根据手动排列的习惯,先固定第一个位置的数字,还剩下最多(n-1)!
种排列,再由放到第一个位置的数字原先的位置i
(从左往右第i
个)的不同,表示跳过了
i
(
n
−
1
)
i(n-1)
i(n−1)种排列。
-
要找到第
k
个排列,先由i = k/(n-1)
得出应该移到第一个位置的数字索引; -
并由
k = k%(n - 1)
更新k; -
这样,我们可以从左往右遍历原先字符串的最小排列,每一次找到应该放在左边第一个位置的数字,将其添加到结果字符串中,并从原字符串中删除,然后对剩下的字符串重复这一操作,直到
k==0
; -
此外由于字符串最初的状态就是第1个排列,所以要将输入的k先减一。
class Solution {
public:
/*
** 根据规律直接找第k个排列
*/
int frac(int k) // k 阶乘
{
int res=1;
while(k)
{
res*=k;
k--;
}
return res;
}
string getPermutation(int n, int k) {
string s=string("123456789").substr(0,n);
string res;
--k;
int i;
while(k)
{
int fra=frac(n-1);
i=k/fra;
res.push_back(s[i]);
s.erase(s.begin()+i);
k=k%fra;
n--;
}
return res+s;
}
};
参考文献
- c++ prime 第5版