全排列算法

来源:互联网 发布:苹果5s2g网络怎么改4g 编辑:程序博客网 时间:2024/05/27 10:44

所谓全排列算法,就是输入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;}
使用该方法比较直观,但是运行效率肯定不高。


2 0