排列的生成

排列和组合的生成在组合数学书中都有,这里是做一个小总结。下面是两种实现方法:

#include <cstdio> #include <memory> #include <algorithm> using namespace std; #define MAX_N 32 int A[MAX_N], used[MAX_N], n, total; void backtrack(int i) { if(i > n) { for(int i = 1; i <= n;i++) printf("%3d", A[i]); total++; printf("/n"); return; } for(int j = i; j<=n;j++) { swap(A[i], A[j]); backtrack(i+1); swap(A[i], A[j]); } } void dfs(int i) { if(i > n) { for(int i = 1; i <= n;i++) printf("%3d", A[i]); printf("/n"); total++; return; } for(int j = 1; j <= n; j++) { if(!used[j]) { A[i] = j; used[j] = 1; dfs(i+1); used[j] = 0; } } } int main() { scanf("%d", &n); freopen("out.txt", "w", stdout); total = 0; for(int i = 1; i <= n; i++) { A[i] = i; } backtrack(1); printf("/ntotal: %d/n", total); fclose(stdout); freopen("out_dfs.txt", "w", stdout); memset(used, 0, sizeof(used)); total = 0; dfs(1); printf("/ntotal: %d/n", total); fclose(stdout); freopen("out_stl.txt", "w", stdout); total = 0; for(int i = 1; i <= n; i++) { A[i] = i; } do { ++total; for(int i = 1; i <= n; i++) { printf("%3d", A[i]); } printf("/n"); } while(next_permutation(A+1,A+n+1)); printf("/ntotal: %d/n", total); fclose(stdout); return 0; }

在STL中,直接有next_permutation可以根据给定的序列生成它按字典序的下一个排列。
上面的代码中用了三种方法生成排列:排列树(backtrack),深度优先搜索(dfs),STL中的算法(next_permutation)。后两种是按照字典序生成排列,第一种生成的排列不是按照字典序的。
第一种的排列树的方法来自于王晓东的《计算机算法设计与分析》教材第5章。这种方法的原理可以用递归式表示为:n! = n * (n-1)!。也就是说,要生成n个元素的全排列,只需要顺次生成以每个元素开头的排列就可以了。生成每个元素开头的排列就是把这个元素放在开头,后面加上剩下的n-1个元素的全排列。用伪代码表示就是:
Perm(A[1..n]) { for(i = 1 to n) { swap(A[1], A[i]); // 把第i个元素放在开头 print(A[1] + Perm(A[2..n])); // 剩下n-1个元素的全排列 swap(A[1], A[i]); // 再交换回来 } }

需要注意的就是这种方式不是按照字典序生成排列的,实际这种方法生成的排列树如下(以1,2,3,4的全排列为例):
排列的生成_第1张图片
上图中括号内表示当前剩余元素。比如4(2,3,1)表示第一层1和4作了交换,现在排在开头的是4,剩下的元素按顺序是2,3,1。1(2,3,4)的子结点4(3,2)表示第2层2和4作了交换,现在排在开头的是1,4,剩下的元素是3,2。这样4(3,2)这棵子树产生的排列按顺序是1,4,3,2; 1,4,2,3。这不是字典序的。

你可能感兴趣的:(算法,permutation)