全排列之递归与非递归算法实现总结

全排列之递归与非递归算法实现总结

 

递归实现

常见的是基于交换的,原理:从而可以推断,设一组数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