(一)生成1~n的排列
分析:用递归的思想解决:先输出所有以1开头的排列(这一步是递归调用),然后输出以2开头的排列(又是递归调用),接着是以3开头的排列……最后才是以n开头的排列。
伪代码:
void print_permutation(序列A, 集合S) {
if(S为空) 输出序列A;
else 按照从小到大的顺序依次考虑S的每个元素v {
print_permutation(在A的末尾填加v后得到的新序列,S-{v});
}
}
下面是实现代码:
#include <iostream>
using namespace std;
const int MAXN = 1000;
int A[MAXN], n;
void print_permutation(int n, int *A, int cur) {
if(cur == n) { //递归边界
for(int i = 0; i < n; ++i) cout << A[i] << " ";
cout << endl;
}else {
for(int i = 1; i <= n; ++i) { //尝试在A[cur]中填各种整数i
int ok = 1;
for(int j = 0; j < cur; ++j) {
if(A[j] == i) ok = 0; //如果i已经在A[0]~A[cur-1]出现过,则不能再选
}
if(ok) { //i在A[0]~A[cur-1]<span style="font-family: Arial, Helvetica, sans-serif;">中没有出现过,所以可以把i放进A[cur]</span>
A[cur] = i;
print_permutation(n, A, cur+1); //递归调用
}
}
}
}
int main() {
cin >> n;
print_permutation(n, A, 0);
return 0;
}
(二)生成可重集的排列
输入数组P,按照字典序输出数组P各元素的所有全排列。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1000;
int A[MAXN], P[MAXN], n;
void print_permutation(int n, int *P, int *A, int cur) {
if(cur == n) {
for(int i = 0; i < n; ++i) cout << A[i] << " ";
cout << endl;
}else for(int i = 0; i < n; ++i) if(!i || P[i] != P[i-1]){ //if条件句确保了不会重复递归调用相同的元素,保证了我们枚举的下标i应不重复不遗漏地取遍所有P[i]值
int c1 = 0, c2 = 0;
for(int j = 0; j < cur; ++j) if(P[i] == A[j]) c1++; //A[0]~A[cur-1]中P[i]出现的次数放在c1中
for(int j = 0; j < n; ++j) if(P[i] == P[j]) c2++; //P数组中P[i]的出现次数放在c2中。
if(c1 < c2) { //只要c1<c2就能递归调用,这样就保证了不会遗漏相同的元素。
A[cur] = P[i];
print_permutation(n, P, A, cur+1);
}
}
}
int main() {
cin >> n;
for(int i = 0; i < n; ++i) cin >> P[i];
sort(P, P+n);
print_permutation(n, P, A, 0);
return 0;
}
(三)生成下一个排列:
枚举所有排列的另一个方法是从字典序最小排列开始,不停的调用“求下一个排列”的过程。
利用STL里面的next_permutation来求下一个排列,
#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXN 1000
using namespace std;
int P[MAXN];
int main(){
int n;
cin >> n;
for(int i = 0; i < n; ++i){
cin >> P[i];
}
sort(P, P+n); // 排序,得到p的最小排列
do{
for(int i = 0; i < n; ++i){
cout << P[i] << " ";
}
cout << endl;
}while(next_permutation(P, P+n));
return 0;
}