所谓全排列算法,就是输入n个数据,然后输出该n个数据的所有的排列情况。
1.每个元素不相同情况下的排列。
排列的用计算机程序的思路来考虑就是递归地往数组A里添加元素的过程,如果放完了元素就输出并回溯。为了形象说明情况,我们举个例子,并给出算法的流程图和程序实现。
demo:输入n,输出1~n的全排列。
流程图:
程序实现:
#include<iostream>
using namespace std;
const int maxn = 10000 + 5;
int A[maxn];
int cur,T;
void show(int* A,int cur)
{
if (cur >= T){
for (int i = 0; i < T; i++){
cout << A[i];
}
cout << endl;
}
else{
for (int i = 1; i <=T; i++){
bool ok = true;
for (int j = 0; j < cur;j++)
if (A[j] == i)ok = false;
if (ok){
A[cur] = i;
show(A, cur + 1);
}
}
}
}
int main()
{
cin >> T;
show(A,0);
system("pause");
return 0;
}
程序运行及及结果:
2.含有相同元素的全排列
1中之所以能根据A中有无元素v来决定放不放入v,是因为输入的元素是相同的。但如果输入的元素含有相同的元素,那么相同的元素就只能放进去一个,问题就出现在是A中虽然已经有元素v了,但输入的元素并没有放完。解决的办法是统计A中元素v出现的次数,如果小于输入元素中v的个数,那么就可以放。对应的算法流程图和程序实现如下。
流程图:
程序实现:
#include<iostream>
using namespace std;
const int maxn = 10000 + 5;
int A[maxn],S[maxn];
int cur,T;
void show(int* A,int cur)
{
if (cur >= T){
for (int i = 0; i < T; i++){
cout << A[i];
}
cout << endl;
}
else{
for (int i = 0; i <T; i++){
int cntA = 0;
int cntS = 0;
for (int j = 0; j < cur;j++)
if (A[j] == S[i])cntA++;
for (int j = 0; j < T;j++)
if (S[j] == S[i])cntS++;
if (cntA<cntS){
A[cur] = S[i];
show(A, cur + 1);
}
}
}
}
int main()
{
cin >> T;
for (int i = 0; i < T; i++)
cin >> S[i];
show(A,0);
system("pause");
return 0;
}
然而,上面的程序当输入
3
1 1 1
的时候,输出的是27个111。很明显答案错了,正确的应该是只输出一个111就好。出现这种错误是因为递归的每一层栈中都三个1都是可以放进A的。比如说第一次调用,先试着把第1个1作为开头,递归调用结束后再尝试用第2个1作为开头,递归调用结束后再尝试用第3个1作为开头,再次递归调用。然而,实际上这3个1都是相同的,应该只递归1次而不是3次。
换句话说,我们枚举的下标i应不重复、不遗漏地取遍所有S[i]值.所以只需检查S的第一个元素和所有“与前面不同”的元素。
由于S数组已经排过序,所以只需检查S的第一个元素和所有“与前一个元素不相同”的元素,即只需在“for(int =0;i<n;i++)”和其后的花括号之前加“if(!i||S[i]!=S[i-1])”即可。代码如下:
#include<iostream>
using namespace std;
const int maxn = 10000 + 5;
int A[maxn],S[maxn];
int cur,T;
void show(int* A,int cur)
{
if (cur >= T){
for (int i = 0; i < T; i++){
cout << A[i];
}
cout << endl;
}
else{
for (int i = 0; i <T; i++)
if (!i || S[i] != S[i - 1]){
int cntA = 0;
int cntS = 0;
for (int j = 0; j < cur; j++)
if (A[j] == S[i])cntA++;
for (int j = 0; j < T; j++)
if (S[j] == S[i])cntS++;
if (cntA < cntS){
A[cur] = S[i];
show(A, cur + 1);
}
}
}
}
int main()
{
cin >> T;
for (int i = 0; i < T; i++)
cin >> S[i];
show(A,0);
system("pause");
return 0;
}
假如S中的元素不是排好序的,只需检查S的第一个元素和“与S[0]~S[i-1]”都不重复的元素。程序实现如下:
#include<iostream>
using namespace std;
const int maxn = 10000 + 5;
int A[maxn],S[maxn];
int cur,T;
void show(int* A,int cur)
{
if (cur >= T){
for (int i = 0; i < T; i++){
cout << A[i];
}
cout << endl;
}
else{
for (int i = 0; i <T; i++){
int cntA = 0;
int cntS = 0;
bool nohaved = true;
for (int j = 0; j < cur; j++)
if (A[j] == S[i])cntA++;
for (int j = 0; j < T; j++)
if (S[j] == S[i])cntS++;
for (int j = 0; j < i;j++)
if (S[j] == S[i])nohaved = false;
if (!i || nohaved){
if (cntA < cntS){
A[cur] = S[i];
show(A, cur + 1);
}
}
}
}
}
int main()
{
cin >> T;
for (int i = 0; i < T; i++)
cin >> S[i];
show(A,0);
system("pause");
return 0;
}
3.next_permutation实现全排列
上面讲的都是通过递归调用来实现全排列的,在C++的头文件中已经有现成生成下一个全排列的程序,只需迭代调用即可。程序实现如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000+5;
int P[maxn];
int main()
{
int T;
cin >> T;
for (int i = 0; i < T; i++)cin >> P[i];
sort(P, P+T);
do{
for (int i = 0; i < T; i++)cout << P[i];
cout << endl;
} while (next_permutation(P, P + T));
system("pause");
return 0;
}
使用该方法比较直观,但是运行效率肯定不高。