generating permunation

generating permunation——全排列(算法汇总)

复制代码

#include <iostream> #include <string> #include <vector> #include <list> #include <queue> #include <iterator> #include <algorithm> using namespace std; #define MAX 10 int used[MAX]; //用来标记数字是否已经在前面使用过 int result[MAX]; //存放结果 int array[MAX] = {1,2,3,4,5,6,7,8,9,10}; void swap(int x, int y){ int temp = array[x]; array[x]=array[y]; array[y]=temp; return; } template<typename T> void printArray(T array[], int size){ int i; for (i=0;i<size;i++) cout << array[i] << " "; cout << endl; return; } /*递归(非字典序)*/ template<typename T> void recur_permute(T array[], int begin, int end) { int i; if (begin==end) { printArray(array, end+1); return; } else{ //for循环遍历该排列中第一个位置的所有可能情况 for (i=begin; i<=end; i++){ swap(begin, i); //循环变换第一个位置 recur_permute(array, begin+1, end); //DFS swap(begin, i); //回溯,保持原排列 } } } /*递归(字典序)*/ template<typename T> void lexi_recur_permute(T array[], int begin, int end) { int i; if (begin == end+1) { printArray(result, end+1); return; } else{ for (i=0; i<=end; i++){ if(!used[i]) //没有标记 { used[i]=1; //标记 result[begin]=array[i]; //记录结果 lexi_recur_permute(array, begin+1, end); used[i]=0; //回溯 } } } } /*STL(字典序)*/ template<typename T> void stl_permute(T array[], int size) { vector<T>::iterator begin, end; vector<T> Pattern(size) ; ostream_iterator<T> out_it(cout, " ") ; //int size=sizeof(array)/sizeof(T); for(int i=0; i<size; i++) Pattern[i]=array[i]; begin = Pattern.begin() ; end = Pattern.end() ; do { copy(begin, end, out_it) ; cout << endl ; }while ( next_permutation(begin, end) ); } int get_factorial(int n) { if(1==n || 0==n) return 1; else return n*get_factorial(n-1); } /*给定元素个数n,以及数组p,返回全排列的序号*/ template<typename T> int perm2num(int n, T *p){ int i, j, num=0,k=1; for (i=n-2;i>=0;i--)//注意下标从0开始 { for (j=i+1;j<n;j++) if (p[j]<p[i]) num+=k;//是否有逆序,如有,统计 k*=n-i; //每一轮后k=(n-i)!, } return num; } /*BFS(字典序)*/ template<typename T> void bfs_permute(T array[], int size) { int idx=0; int cnt=get_factorial(size); list<T> ls; queue<list<T>> q; ls.push_back(array[0]); q.push(ls); while(!q.empty()) { list<T> cur_perm = q.front(); if(cur_perm.size() == size) //第n层的第一个元素长度等于size,循环结束 break; if(cur_perm.size() != idx) //不相等 idx++; q.pop(); list<T>::iterator it = cur_perm.end(); while( it!=cur_perm.begin() ) { cur_perm.insert(it, array[idx]); //插入 q.push(cur_perm); it=cur_perm.erase(--it); //还原 --it; //向前一步找插入点 if( it==cur_perm.begin() ) //第一个插入点特殊处理并结束 { cur_perm.push_front(array[idx]); q.push(cur_perm); cur_perm.clear(); break; } } } print_queue(q, size, cnt); } template<typename T> void print_queue(queue<list<T>> q, int size, int cnt) { vector<list<T>> vec(cnt); T* perm=new T[size]; //存储当前排列 /*映射*/ while(!q.empty()) { list<T> cur_perm=q.front(); q.pop(); list<T>::iterator it=cur_perm.begin(); int idx=0,i=0; int n=size; while(it!=cur_perm.end()) { perm[i++]=*it++; } //当前排列放入全排列对应位置 idx=perm2num(size, perm); vec[idx]=cur_perm; } delete []perm; /*输出*/ vector<list<T>>::iterator vit=vec.begin(); for(;vit!=vec.end();vit++) { list<T> cur_perm=*vit; list<T>::iterator lit=cur_perm.begin(); for(;lit!=cur_perm.end();lit++) { cout<<*lit<<" "; } cout<<endl; } } int main(){ recur_permute(array, 0, 3); lexi_recur_permute(array, 0,3); stl_permute(array, 4); bfs_permute(array, 4); return 0; }
复制代码

 

上面一共提供了4种全排列的方法,包括递归非字典序版本、递归字典序版本、标准库版本和BFS字典序版本,当然BFS非字典序实现相对于BFS字典序版本更加简洁,稍加修改即可。

说明:递归版本基于网上现有代码修改而成,标准库版本参照msdn sample修改而成,最后的BFS版本是由本人在看到题目后思考而来,并实现之(递归版本很久之前写过),所有四种算法都加了模板。当然BFS版本效率相对于递归要快,相对于STL版本则较慢,仅仅提供一种思路而已

 

最后再附上STL版本算法思路及修改后的代码(仅仅为了可读性):

 

思路

给定已知序列P =  A1A2A3.....An
对P按字典排序,得到P的一个最小排列Pmin = A1A2A3....An ,满足Ai > A(i-1) (1 < i <= n)
从Pmin开始,找到刚好比Pmin大的一个排列P(min+1),再找到刚好比P(min+1)大的一个排列,如此重复。
1.从后向前(即从An->A1),找到第一对为升序的相邻元素,即Ai < A(i+1)。
  若找不到这样的Ai,说明已经找到最后一个全排列,可以返回了。
2.从后向前,找到第一个比Ai大的数Aj,交换Ai和Aj。
3.将排列中A(i+1)A(i+2)....An这个序列的数逆序倒置,即An.....A(i+2)A(i+1)。因为由前面第1、2可以得知,A(i+1)>=A(i+2)>=.....>=An,这为一个升序序列,应将该序列逆序倒置,所得到的新排列才刚刚好比上个排列大。
4.重复步骤1-3,直到返回。
这个算法是C++ STL算法next_permutation的思想。
 
代码
复制代码

你可能感兴趣的:(NAT)