全排列的编码与解码——康托展开 (附完整代码)

本文介绍全排列的编码与解码方法——康托展开,详细解释如何通过康托展开实现从全排列到自然数的双射转换,以及如何进行全排列的编码与解码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全排列的编码与解码——康托展开 (附完整代码)
2013-11-27      我来说两句       作者:synapse7
收藏     我要投稿
一、康托展开:全排列到一个自然数的双射
 
X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
 
ai为整数,并且0<=ai<i(1<=i<=n)
 
 适用范围:没有重复元素的全排列
 
 
二、全排列的编码:
 
{1,2,3,4,...,n}的排列总共有n!种,将它们从小到大排序,怎样知道其中一种排列是有序序列中的第几个?
 
如 {1,2,3} 按从小到大排列一共6个:123 132 213 231 312 321。想知道321是{1,2,3}中第几个大的数。
 
这样考虑:第一位是3,小于3的数有1、2 。所以有2*2!个。再看小于第二位,小于2的数只有一个就是1 ,所以有1*1!=1 所以小于32
 
的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个大的数。2*2!+1*1!是康托展开。(注意判断排列是第几个时要在康托展开的结果后+1)
 
再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个,0*3!,第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2,1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数,0*1!,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。
 
又例如,排列3 5 7 4 1 2 9 6 8展开为98884,因为X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884.
 
解释:
 
排列的第一位是3,比3小的数有两个,以这样的数开始的排列有8!个,因此第一项为2*8!
 
排列的第二位是5,比5小的数有1、2、3、4,由于3已经出现,因此共有3个比5小的数,这样的排列有7!个,因此第二项为3*7!
 
以此类推,直至0*0!
 
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<cstdio> 
const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320}; ///阶乘 
   
int KT( int s[], int n) 
     int i, j, cnt, sum; 
     sum = 0; 
     for (i = 0; i < n; ++i) 
    
         cnt = 0; 
         for (j = i + 1; j < n; ++j) 
             if (s[j] < s[i]) ++cnt; 
         sum += cnt * fac[n - i - 1]; 
    
     return sum; 
   
int main() 
     int a[] = {3, 5, 7, 4, 1, 2, 9, 6, 8}; 
     printf ( "%d\n" , 1 + KT(a, sizeof (a) / sizeof (*a))); ///1+98884 

 

三、全排列的解码
如何找出第16个(按字典序的){1,2,3,4,5}的全排列?
 
1. 首先用16-1得到15
 
2. 用15去除4! 得到0余15
 
3. 用15去除3! 得到2余3
 
4. 用3去除2! 得到1余1
 
5. 用1去除1! 得到1余0
 
有0个数比它小的数是1,所以第一位是1
 
有2个数比它小的数是3,但1已经在之前出现过了所以是4
 
有1个数比它小的数是2,但1已经在之前出现过了所以是3
 
有1个数比它小的数是2,但1,3,4都出现过了所以是5
 
最后一个数只能是2
 
所以排列为1 4 3 5 2
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<cstdio> 
#include<cstring> 
const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320}; ///阶乘 
   
bool vis[10]; 
   
///n为ans大小,k为全排列的编码 
void invKT( int ans[], int n, int k) 
     int i, j, t; 
     memset (vis, 0, sizeof (vis)); 
     --k; 
     for (i = 0; i < n; ++i) 
    
         t = k / fac[n - i - 1]; 
         for (j = 1; j <= n; j++) 
             if (!vis[j]) 
            
                 if (t == 0) break
                 --t; 
            
         ans[i] = j, vis[j] = true
         k %= fac[n - i - 1]; ///余数 
    
   
int main() 
     int a[10]; 
     invKT(a, 5, 16); 
     for ( int i = 0; i < 5; ++i) 
         printf ( "%d " , a[i]); ///1 4 3 5 2 

 

 2.1.12 Next Permutation
描述
Implement next permutation, which rearranges numbers into the lexicographically next greater permutation
of numbers.

If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
e replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the le-hand column and its corresponding outputs are in the
right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
分析
算法过程如图2-1所示(来自http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html)。
图2-1 下一个排列算法流程
代码
// LeetCode, Next Permutation
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
void nextPermutation(vector<int> &num) {
next_permutation(num.begin(), num.end());
}
template<typename BidiIt>
bool next_permutation(BidiIt first, BidiIt last) {
// Get a reversed range to simplify reversed traversal.
const auto rfirst = reverse_iterator<BidiIt>(last);
const auto rlast = reverse_iterator<BidiIt>(first);
// Begin from the second last element to the first element.
auto pivot = next(rfirst);
// Find `pivot`, which is the first element that is no less than its
// successor. `Prev` is used since `pivort` is a `reversed_iterator`.
while (pivot != rlast && *pivot >= *prev(pivot))
++pivot;
// No such elemenet found, current sequence is already the largest
// permutation, then rearrange to the first permutation and return false.
if (pivot == rlast) {
reverse(rfirst, rlast);
return false;
}
// Scan from right to left, find the first element that is greater than
// `pivot`.
auto change = find_if(rfirst, pivot, bind1st(less<int>(), *pivot));
swap(*change, *pivot);
reverse(rfirst, pivot);
return true;
}
};
相关题目
• Permutation Sequence, 见§2.1.13
• Permutations, 见§8.3
• Permutations II, 见§8.4
• Combinations, 见§8.5
2.1.13 Permutation Sequence
描述
e 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):
20 第2 章线性表
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note: Given n will be between 1 and 9 inclusive.
分析
简单的,可以用暴力枚举法,调用k
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿拉平平的小屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值