n个数的全排列,按照从下到大的顺序排列,问第k个位置的数是多少?
这个问题有几种方法,不过只有康托编码的时间复杂度是最低的。是O(n),简直凶残。
1.用next_permutation来求,这个也是leetcode上的一道题,就是求其下一个排列,然后对1234…n 运行k-1次 next_pernutation方法就能得到第k个数了
思想就是这样。
2.利用康托编码来求。
其实就是康托展开的逆过程。
康托展开用来求某个全排列数是第几小的数,也就是当这些数按顺序排时第几个数。
过程如下:比如求321 是 第几小的,可以这样来想:小于3的数有1和2 两个,首位确定之后后面两位有2!中情况,所以共有2*2!=4种。
小于2的数只有一个1,所以有1*1!=1种情况,最后一位是1,没有比一小的数,所以是0*0!=0
综上:小于321的数有4+1=5个,所以321是第六小的数。
逆过程就是已知这个数是第k个数,求这个数是多少,当然是知道n的值的。
第k个数就是有k-1个数比这个数小。
所以就是 k-1=an*(n-1)!+an-1*(n-2)!+....+a1*0!;
简单的,可以用暴力枚举法,调用k -1 次next_permutation()。
暴力枚举法把前k 个排列都求出来了,比较浪费,而我们只需要第k 个排列。
2.
利用康托编码的思路,假设有n 个不重复的元素,第k 个排列是a1; a2; a3; :::; an,那么a1 是
哪一个位置呢?
我们把a1 去掉,那么剩下的排列为a2; a3; :::; an, 共计n-1 个元素,n-1 个元素共有(n-1)!
个排列,于是就可以知道a1 = k/(n - 1)!。
同理,a2; a3; :::; an 的值推导如下:
k2 = k%(n - 1)!
a2 = k2/(n - 2)!
kn-1 = kn-2%2!
an-1 = kn-1/1!
an = 0
#include <iostream>
using namespace std;
#include <string>
#include <vector>
void nextPermutation(string & v)
{
int end = v.size()-1;
int i = end ,k= end;
while(i)
{
if (v[i]>v[i-1])
break;
--i;
}
if (i>0)
{
--i;
while(v[k]<=v[i])
--k;
swap(v[i],v[k]);
reverse( v.begin()+i+1, v.end() );
}
else
{
reverse( v.begin(),v.end() );
}
}
// 调用k-1次Next Permutation
string getKthPermutation(int n ,int k)
{
string s(n,'0');
for ( int i =0; i<n;++i)
{
s[i] += i+1;
}
int j = k-1;
while (j--)
{
nextPermutation( s);
}
return s;
}
int factorial (int n)
{
int result = 1;
for (int i = 1; i<= n;++i)
{
result *= i;
}
return result ;
}
//康托编码
template <typename sequence >
sequence kth_Permutation(const sequence & seq ,int k)
{
const int n = seq.size();
sequence s(seq);
sequence result;
int base = factorial (n-1);
--k;//康托编码是从0开始,即是s[0]下标是从0开始的,为一致,--k
for (int i = n-1; i>0; k %= base, base /= i,--i)
{
auto a = next(s.begin(),k/base);//找到第n位上的数字在排列中的位置index,这样提取的数列中的index位
result.push_back(*a); //再push
s.erase(a);//排列中数字不能重复,所以必须删除元素*a
}
result.push_back(s[0]);//,注意for循环是n-1到1,只push n-1个元素,所以还要push最后一个
return result;
}
string getPermutation(int n , int k)
{
string s(n,'0');
string result;
for ( int i =0; i<n;++i)
{
s[i] += i+1;
}
return kth_Permutation(s, k);
}
int main()
{
int n = 5, k= 2;
//test1 康托编码
string s ;
s = getPermutation(n, k);
cout<<s;
//test2 调用netx permutation
string s2;
s2 = getKthPermutation(n,k);
cout<<s2;
return 0;
}