#全排列的两种方法#
1.【解答树】
假设序列为{1,2,3,4},形成全排列。解答树显示出了递归函数的调用过程。
可以近似看成深搜,一层层的解决问题。
这棵树和前面介绍过的二叉树不同。第0层(根)结点有n个子结点,第1层结点各有n-1个子结点,第2层结点各有n-2个子结点,第3层结点各有n-3个子结点,……,第n层结点都没有子结点(即都是叶子),而每个叶子对应于一个排列,共有n!个叶子。由于这棵树展示的是从“什么都没做”逐步生成完整解的过程,因此将其称为解答树。
提示:如果某问题的解可以由多个步骤得到,而每个步骤都有若干种选择(这些候选方案集可能会依赖于先前作出的选择),且可以用递归枚举法实现,则它的工作方式可以用解答树来描述。
这棵解答树一共有多少个结点呢?可以逐层查看:第0层有1个结点,第1层n个,第2层有n*(n-1)个结点(因为第1层的每个结点都有n-1个结点),第3层有n*(n-1)*(n-2)个(因为第2层的每个结点都有n-2个结点),……,第n层有n*(n-1)*(n-2)*…*2*1=n!个。将其求和。
由于叶子近似有n!个,倒数第二层也有n!个结点,因此上面的各层全部加起来也不到n!。
这是一个很重要的结论:在多数情况下,解答树上的结点几乎全部来源于最后一两层。
和它们相比,上面的结点数可以忽略不计。
利用解答树的深度优先遍历实现数列的全排序。
例如 1 2 3 ------>1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1共6种不同的排序
参考代码如下:
#include <string.h>
#include <iostream>
using namespace std;
const int N = 99999999; //输入排序的个数的最大值
int record[N]; //记录每次排序的序列
int visited[N]; //标记节点是否被访问过
int n; //输入节点的数目
int totalSize = 0;
void DFS(int start){
if(start>=n){ //递归出口
for(int i=0;i<n;i++){
cout<<record[i]<<" ";
}
totalSize++;
cout<<endl;
return;
}
for(int i=1;i<=n;i++){ //深度遍历节点,并标记已经访问过的节点
if(visited[i]==0){
visited[i] = 1;
record[start] = i;
DFS(start+1); //递归遍历
visited[i] = 0; //回退时标记回退的节点为未被访问节点
}
}
}
int main()
{
cin>>n;
memset(visited,0,n);
DFS(0);
cout<<"totalSize = "<<totalSize<<endl;
return 0;
}
2.【下一个排列】next_permutation函数
枚举所有排列的另一个方法是从字典序最小排列开始,不停调用“求下一个排列”的过程。
如何求下一个排列呢?C++的STL中提供了一个库函数next_permutation。看看下面的代码片段,就会明白如何使用它了。
#include<cstdio>
#include<algorithm> //包含next_permutation
using namespace std;
int main( ) {
int n, p[10];
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &p[i]);
sort(p, p+n); //排序,得到p的最小排列
do {
for(int i = 0; i < n; i++) printf("%d ", p[i]); //输出排列p
printf("\n");
} while(next_permutation(p, p+n)); //求下一个排列
return 0;
}
需要注意的是,上述代码同样适用于可重集,但只能用于全排列。
枚举所有排列的另一个方法是从字典序最小排列开始,不停调用“求下一个排列”的过程。
如何求下一个排列呢?C++的STL中提供了一个库函数next_permutation。看看下面的代码片段,就会明白如何使用它了。
#include<cstdio>
#include<algorithm> //包含next_permutation
using namespace std;
int main( ) {
int n, p[10];
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &p[i]);
sort(p, p+n); //排序,得到p的最小排列
do {
for(int i = 0; i < n; i++) printf("%d ", p[i]); //输出排列p
printf("\n");
} while(next_permutation(p, p+n)); //求下一个排列
return 0;
}
需要注意的是,上述代码同样适用于可重集,但只能用于全排列。