PTA数据结构 习题2.8 输出全排列 (20分)
习题2.8 输出全排列 (20分)
请编写程序输出前n个正整数的全排列(n<10),并通过9个测试用例(即n从1到9)观察n逐步增大时程序的运行时间。
输入格式:
输入给出正整数n(<10)。
输出格式:
输出1到n的全排列。每种排列占一行,数字间无空格。排列的输出顺序为字典序,即序列a1,a2,⋯,an排在序列b1,b2,⋯,bn之前,如果存在k使得a1=b1,⋯,ak=bk并且 ak+1 < bk+1。
输入样例:
3
输出样例:
123
132
213
231
312
321
全排列字典序算法
全排列生成算法
所谓字典序,就是将元素按照字典的顺序(a-z,1-9)进行排列。strcmp
就是以字典序来比较字符串。
字典序算法步骤
设P是集合
{
1
,
2
,
.
.
.
.
.
.
n
−
1
,
n
}
\{1,2,......n-1,n\}
{1,2,......n−1,n}
的一个全排列:P0P1…Pj-1PjPj+1…Pn
集合必须按照递增顺序排列好
- 从下标
n
开始递减,找出第一个比右边数字小的数字序号i
P i < P i + 1 P_i<P_{i+1} Pi<Pi+1 - 在Pi右边的数字里,找到所有比Pj大的数字中最小的数字Pk,即
k = min { i ∣ P i > P j , i > j } k=\min\{i|P_i>P_j,i>j\} k=min{i∣Pi>Pj,i>j} - 交换Pi,Pj
- 将
i
右边的序列Pi+1Pi+2…Pn逆置,变为PnPn-1…Pi+1
C++代码如下
#include <iostream>
#include <cstdio>
int
fact( int n );
void
dictSeq( int a[], int n );
void
reverse( int a[], int n, int j );
void
swaq( int &a, int &b );
void
print_array( int a[], int n );
int
main( void )
{
int *a;
int n;
scanf("%d", &n);
a = new int[n];
for( int i = 0; i < n; i++ ){
a[i] = i+1;
}
int total = fact(n); //全排列个数等于n!
print_array( a, n ); //初始序列算一种排列, 在交换之前单独输出
for( int i = 1; i < total; i++ ){ //让dictSeq执行total-1次, 输出除初始序列之后的所有可能
dictSeq( a, n );
}
return 0;
}
void
dictSeq( int a[], int n )
{
int i;
for( i = n-1; i > 0; i-- ){
if( a[i-1] < a[i] ){
i--;
break;
}
}
int min_j = i + 1;
int min = a[min_j];
for( int j = min_j; j < n; j++ ){
if( a[j] > a[i] && min > a[j] ){
min = a[j];
min_j = j;
}
}
swaq( a[min_j], a[i] );
reverse( a, n, i );
print_array( a, n );
}
void
reverse( int a[], int n, int i )
{
int left = i + 1, right = n - 1;
while( left < right ){
swaq( a[left++], a[right--]);
}
}
int
fact( int n )
{
int fact = 1;
do{
fact *= n--;
}while( n > 0 );
return fact;
}
void
swaq( int &a, int &b )
{
int temp;
temp = a;
a = b;
b = temp;
}
void
print_array( int a[], int n )
{
for( int i = 0; i < n; i++ ){
printf("%d", a[i]);
}
printf("\n");
}