问题描述:
设计一个递归算法生成n个元素{r1,r2,…,rn}的全排列。
全排列:将n个不同元素按一定的顺序排列起来,所能形成的所有排列情况
算法思路:
设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}。
集合X中元素的全排列记为perm(X)。
(ri)perm(X)表示在全排列perm(X)的每一个排列前加上前缀得到的排列。R的全排列可归纳定义如下:
当n=1时,perm(R)=(r),其中r是集合R中唯一的元素;
当n>1时,perm(R)由(r1)perm(r1),(r2)perm(r2),…,(rn)perm(rn)构成。
为什么这么设,我们要遍历所有排列情况,先把第一个元素放在第一位,然后剩下的所有元素中依次填入第二位,以此类推,求出的是第一个元素在首位的所有情况,剩下的所有情况与之步骤相似,依次求出第二、第三到第n个元素在首位时的所有情况,此时遍历完成。
例如:1 2 3的全排列
先把1放在首位,然后剩下的有两种情况,2或3放在第二位,剩下的那个数放在第三位,这样就遍历了所有1在首位的情况;
然后同第一步,依次把2、3放在首位,求出剩余元素的排列情况;
此时所有情况相加就是总的全排列的情况
核心代码:
Void Perm(Type list[],int k,int m){
//k为头,m为尾
if(k==m){//为真即表示剩余最后一个元素,此时输出排列情况
for(int i=0;i<=m;i++){
cout<<list[i];
}
}
else{
for(int i=k;i<=m;i++){//从k到m,依次把每个数放在当前所有元素的首位,然后求剩余元素的所有排列情况
swap(list[k],list[i]);//依次放在当前所有元素首位k,同时原本首位的值暂时先放到它空缺的位置上算做剩余元素
perm(list,k+1,m);//剩余的所有元素求全排列,继续把每个元素依次放在当前的首位,进行递归
swap(list[k],list[i]);//完成之后再把之前暂时放在首位的值交换回去,变回原位,便于下一个数的操作
}
}
}
总结:
递归算法就是把问题大而化之,就像本题,求全排列的所有情况,我们的遍历方式就是依次把每个数放在第一位,然后剩余元素继续全排列,剩余元素求全排列继续这样遍历,这里就是几乎相同的操作了,这样就可以一步步递归,从而把问题分解。当然,递归里面非常重要的前提是弄清楚边界,这道题里的边界就是km,即头尾指针相等时,只剩一个元素了,这个时候递归就结束了。