引入
首先,让我们来看一个题
题目:康托展开
题目描述:给出一个数N,再给出N的全排列的某一个排列,问该排列在全排列中的次序是多少?例如3的全排列中,123排第一位,321排最后一位
输入第一行为一个数N,第二行为N的全排列的某一个排列
输出一个整数,表示该排列在全排列中的次序
样例:输入:3
1 2 3
输出:1
题目理解
很多人看到这个题目会感到十分的简单,只需要将这个数的全排列求出来,之后遍历求出来的数组并输出问题位置就好,但我们要知道,之前全排列的方法的时间复杂度是O(n!),也就是说如果我们要在这个题上用之前的方法,遇到较大的数就会空间或时间超限。那么,我们就可以引入我们的康托展开了
康托展开
基本概念
对于康拓展开,百度百科(文言文)上是这样说的:康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。 康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
这句话并不是很难理解,就是将一个全排列用康托展开压缩成一个数(他的位置)进行存储,同时也可以将这个数进行康托的逆运算求出全排列。
压缩方法
对于康托展开我们有一个公式:
X
=
a
n
×
(
n
−
1
)
!
+
a
n
−
1
×
(
n
−
2
)
!
+
⋯
+
a
1
×
0
!
X=a_n \times(n-1)!+a_{n-1} \times(n-2)!+\cdots+a_1 \times 0!
X=an×(n−1)!+an−1×(n−2)!+⋯+a1×0!
我们可以举一个例子:在
(
1
,
2
,
3
,
4
,
5
)
(1,2,3,4,5)
(1,2,3,4,5)的全排列中,求34152的康托展开值。
首位是3,则小于3的数有两个,为1和2,
a
[
5
]
=
2
a[5]=2
a[5]=2,则首位小于3的所有排列组合为
a
[
5
]
∗
(
5
=
1
)
!
a[5]*(5=1)!
a[5]∗(5=1)!
第二位是4,由于第一位小于4,1、2、3中一定会有1个充当第一位,所以排在4之下的只剩2个,所以其实计算的是在第二位之后小于4的个数。因此
a
[
4
]
=
2
a[4]=2
a[4]=2。
第三位是1,则在其之后小于1的数有0个,所以
a
[
3
]
=
0
a[3]=0
a[3]=0。
第四位是5,则在其之后小于5的数有1个,为2,所以
a
[
2
]
=
1
a[2]=1
a[2]=1。
最后一位就不用计算啦,因为在它之后已经没有数了,所以a[1]固定为0
对于公式来说:
X
=
2
×
4
!
+
2
×
3
!
+
0
×
2
!
+
1
×
1
!
+
0
×
0
!
=
61
X=2 \times4!+2 \times3!+0 \times2!+1 \times1!+0 \times0!=61
X=2×4!+2×3!+0×2!+1×1!+0×0!=61,所以比34152小的排列有61个,即34152是第62个
模板代码
对于康托展开,也是有一套固定的模板代码的
int num[1000];
long long kt(int a[],int n){
long long s=1;
long long p[1000];
for(int i=0;i<n-i;i++){ //计算阶乘(如果题目要求小可以直接用数组写出)
s*=i+1;
p[i]=s;
}
long long sum=1;
for(int j=i+1;j<=n;j++){ //套用公式
if(num[i]<num[j]{
s++;
}
sum+=s*p[n-2-i];
}
return sum; //此时sum即为康托展开值
}
总结
这便是康托展开的基本运用,如果有错误欢迎指正,会尽力修改完善