medium程度题
题目:
The set [1,2,3,…,n]
contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):
"123"
"132"
"213"
"231"
"312"
"321"
容易想到的是调用k - 1次上一题leetcode31. Next Permutation的函数,复杂度O( (K - 1) * N )
参考别人的解法,知道了康托展开
康托展开用来求一个数是全排列的第几小数
如 52314
比5小的数有4个
比2小的数有1个
比3小的数有2个,因为2已经在左边固定了,所以有1个
比1小的数没有
比4小的数有三个,1,2,3在左边都已经固定,故有0个
K = 4 * 4! + 1 * 3! + 1 * 2! + 0 * 1! + 0 * 0!
因此52314是{1,2,3,4,5}全排列的第 K + 1 = 105小的数
反过来已知n,K求排列,康托逆展开,过程如下
K - 1 = 104
104 / 4! = 4 103 % 4!= 8
8 / 3! = 1 8 % 3! = 2
2 / 2! = 1 1 % 2! = 0
0 / 0! = 0 0 % 0! = 0
0 / 0! = 0 0 % 0! = 0
比4大的数为5
比1大的数为2
比1大的数为2,因为2已经出现故为3
比0大的数为1
比0大的数为1,因为1,2,3已经出现,故为4
所以这个数为52314
我的解法是构建一个used数组用来记录数字是否被使用过,假如当前求得第i位的商为K
那么在序列{1,2,3...n}中,从前往后第K个未被使用的数就是第i位的值,因为我们要确保
第i + 1位到第n位恰好有K个数小于第i位的数
复杂度为O(n ^2)
AC解
class Solution {
public:
string getPermutation(int n, int k)
{
string s(n,'0');
for (int i = 0; i <= n; i++)
s[i] += i + 1;
return kth_permutation(s,n,k);
}
//求阶乘
int fac(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
result *= i;
return result;
}
string kth_permutation(string s,int n,int k)
{
string result;
int base = fac(n - 1);
k--;
bool used[n + 1];
for (int i = 1; i < n + 1; i++)
used[i] = false;
for (int i = 1; i < n; i++)
{
int m = k / base;
k %= base;
base /= (n -i);
//从used数组中找m个未使用的数,第m + 1个未使用的数就是第i位的值
int a = 0,index;
for (int i = 1; i < n + 1; i++)
{
if (!used[i])
a++;
if (a == m + 1)
{
index = i;
break;
}
}
result.push_back(s[index - 1]);
used[index] = true;
}
//最后一个未被使用的数放入字符串尾
int a;
for (int i = 1; i < n + 1; i++)
if (!used[i])
{
a = i - 1;
break;
}
result.push_back(s[a]);
return result;
}
};
参考了别人的解法,复杂度为O(n),真是精妙
AC解:
class Solution {
public:
string getPermutation(int n, int k)
{
string s(n,'0');
for (int i = 0; i <= n; i++)
s[i] += i + 1;
return kth_permutation(s,n,k);
}
int fac(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
result *= i;
return result;
}
string kth_permutation(string s,int n,int k)
{
string result;
int base = fac(n - 1);
k--;
for (int i = n - 1; i > 0; k %= base,base /= i,i--)
{
int m = k / base;
result.push_back(*(s.begin() + m));
s.erase(s.begin() + m);
}
result.push_back(s[0]);
return result;
}
};
直接将使用了的擦除,避免了每次对数组进行搜索,极大的降低了复杂度,妙!
路漫漫其修远兮,中途试了n次,改了几个小时才想到最上面的解法,别人的解法是多么
的优美,要多思考,多总结,多练啊!