全排列之递归与非递归算法实现总结
递归实现
常见的是基于交换的,原理:从而可以推断,设一组数p = {r1, r2, r3, ... ,rn}, 全排列为perm(p),pn = p - {rn}。
因此perm(p) = r1perm(p1), r2perm(p2), r3perm(p3), ... , rnperm(pn)。当n = 1时perm(p} = r1。意思即是,将整组数中的所有的数分别与第一个数交换,这样就总是在处理后n-1个数的全排列, 如果有重复数字,则第一个数每次与后面的不同数字交换;
关键代码
Void perm(list,k,m) { If(k>m) print(list) Else { For(i=k; i<=m;i++) { Swap(&list[k],&list[i]) Perm(list,k+1,m) Swap(&list[k],&list[i]) } } }
上面这种方法可以顺序打印出所有排列,但是无序的,也无法打印出第n个排列,下面这种递归实现可以升序打印出所有排列;如果要打印第n个排列,加一个计数器就可以,但需要先生成前n-1个排列,不划算;
Vector result; //存放一个排列 Bool visited; //初始化为false Void perm(list,n) { If(result.size() >= n) print(result) Else { For(i=0; i<n;i++) { If(visited[i] ) continue; Visited[i] = true; Perm(list,m) Visited[i] = false; } }}
全排列的非递归实现可以借助下面的两个方法(代码略):
方法一:生成下一个排列,该方法对重复元素同样有效
如果可以根据一个排列生成他的下一个排列,那么生成所有排列也就不在话下了,下面以排列625431为例来说明怎么生成下一个排列,首先从右向左找到第一个降序对,这里是25,然后将前面的数字与其后面的大于它的最小数字相替换,这里是指将2与2后面的大于2的最小数字相替换,被替换数字是3,替换后的序列是635421,此时3后面的数字肯定是一个降序(从左到右)序列,将这个序列颠倒即可,得到631245,这就是625431的下一个序列,初始时应将元素排序,然后逐个生成;
方法二:生成第n个排列
m个元素的排列总数为m!; 假设m个元素初始时已经升序放入集合key中,则第n个排列的第一个元素必然是集合key中第n/(m-1)! 小元素,记为kx, 设定最小元素为第0小元素;接下来在集合key-{kx}中找出第n%(m-1)!个排列;以此类推,迭代找出所求排列的所有元素;
如果有重复元素,则m个元素的排列总数为m1! /(c1! * c2! *….cn!),ci为元素ki的重复次数;集合key也是一个多重集,对重复元素每次也只删除一个;
参考来源
http://www.cnblogs.com/nokiaguy/archive/2008/05/11/1191914.html
http://blog.csdn.net/morewindows/article/details/7370155/
来源:https://www.cnblogs.com/gaoyanqing/p/4312962.html