康托展开式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]表示在未出现的元素中比当前元素小的个数。康托展开式可用于求一个排列位于全排列的第几个。同样,其逆过程可以求在全排列中的第k个排列是多少。具体实现如下:
例题1:
给出一个数字n和一个排列,返回这个排列在数字1-n的全排列(按字典序)中排第几位。其中,编号从1开始。例如,排列 [1,2,3]
在数字{1,2,3}的全排列中是第 1
个排列,排列[1, 3, 2]在数字{1,2,3}的全排列中是第2个排列。以此类推,以下是数字{1,2,3}的全排列及其编号:
1 2 3 | 1
1 3 2 | 2
2 1 3 | 3
2 3 1 | 4
3 1 2 | 5
3 2 1 | 6
1 int getIndex(int n, vector<int> &numbers) 2 { 3 if(n<=0 || numbers.size()!=n)return -1; 4 if(n==1)return 1; 5 vector<int> fact(n, 1);//先生成阶乘数组 6 for(int i=1; i<fact.size(); ++i)fact[i]=fact[i-1]*i; 7 int sum=0; 8 for(int i=0; i<numbers.size(); ++i) 9 { 10 int count=0; 11 for(int j=i+1; j<numbers.size(); ++j) 12 { 13 if(numbers[j]<numbers[i])++count; 14 } 15 sum=sum+count*fact[n-i-1]; 16 } 17 return sum+1; 18 }
康托展开式的逆过程的求法也可以作为求1至n的数字的全排列的非递归实现方法,其逆过程可实现为
1 vector<int> getArray(int n, int k) 2 { 3 if(n<=0)return vector<int>(); 4 vector<int> fact(n+1, 1);//先生成阶乘数组 5 for(int i=1; i<fact.size(); ++i)fact[i]=fact[i-1]*i; 6 if(k>fact[n])return vector<int>(); 7 --k; 8 vector<bool> flag(n+1, false);//标记哪个数字没用过 9 vector<int> result; 10 for(int i=0; i<n; ++i) 11 { 12 int count=k/fact[n-i-1]; 13 for(int i=1; i<flag.size(); ++i) 14 { 15 if(flag[i]==false)--count; 16 if(count==-1){ 17 flag[i]=true; 18 result.push_back(i); 19 break; 20 } 21 } 22 k=k%fact[n-i-1]; 23 } 24 return result; 25 }