全排列问题:
给定n个字符{r1,r2,…,rn},要求生成这n个字符的全排序
算法思想:
设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}。
集合X中元素的全排列记为perm(X)。
(ri)perm(X)表示在全排列perm(X)的每一个排列前加上前缀得到的排列。
R的全排列可归纳定义如下:
(1)当n=1时,perm(R)=(r),其中r是集合R中唯一的元素;
(2)当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),…,(rn)perm(Rn)构成。
算法分析
1、假设有两个数 1,2,他们的全排列是 1,2和2,1,即以1开头的2的全排列和以2开头的1的全排列,而1个数的全排列是其本身,即定义(1)部分。
2、假设有三个数1,2,3,他们的全排列是 1,2,3;1,3,2;2,1,3;2,3,1;3,1,2;3,2,1六组,即定义(2)部分。
3、定义(2)部分可以理解为:将整个数列中每一个数分别与第一个数交换后加其他数的全排列,因此每次都是处理后n-1个数的全排列
4、123的全排列:首先遍历所有元素,然后把遍历到的每一个元素和第一个元素交换:
(1)1和1交换还是1,那么就有了1,(2,3)
(2)同理,2,3和第一个交换后有了2,(1,3)和3,(1,2)共3组
(3)交换完了后要交换回来,因为后面交换都是在原来的数列1,2,3的基础上交换的,所以在排列前要交换一次,排列后还要交换一次(还原)
(4)检查每组除了第一个元素之外剩余的元素,如果元素的个数是2,那么就对剩下的元素进行全排列(递归)。
(5)1(2,3)按第1~4步得到1,2,3;1,3,2
5、为什么要交换两次?会漏掉很多组合
执行步骤
求n个元素a1,a2 ….an的全排列:由排列组合的知识可知,n个元素的全排列有n!种=n*(n-1)!种=n*(n-1)*(n-2)!种=….
分析 : n=1 out: a1
n=2 out: a1,a2
a2,a1
n=3 out: a1,a2,a3
a1,a3,a2
a2,a1,a3
a2,a3,a1
a3,a1,a2
a3,a2,a1
上述步骤可以用循环执行“交换位置,后跟剩余的序列的全排列”;对剩余的序列再次使用该方法,直至没有剩余序列(只有一个元素)
#include<iostream>
#include <math.h>
using namespace std;
void perm(char ch[],int s,int t)
{
int i;
if(s==t)
{
for(i=0;i<=t;i++)
cout<<ch[i];
cout<<endl;
}
else
{
for(i=s;i<=t;i++)
{
swap(ch[i],ch[s]);
perm(ch,s+1,t);
swap(ch[i],ch[s]);
}
}
}
int main()
{
int i,n,m;
char ch[1000];
cin>>n;
while(n--)
{
cin>>m;
for(i=0;i<m;i++)
cin>>ch[i];
perm(ch,0,m-1);
if(n) cout<<endl;
}
}