5.邻位互换法
是由Johnson-Trotter首先提出。如果已知n-1个元素的排列,将n插入到排列的不同位置,就得到了n个元素的排列。用这种方法可以产生出任意n个元素的排列。但是,为了产生n个元素的排列,我们必须知道并存储所有n-1个元素的排列,然后才能产生出所有n阶排列,这是一个很大的缺点。
该算法的实质是交换排列中某两个相邻的元素来产生一个新的排列。按照下面所说的算法就可以从原始排列开始,生成全体排列。具体算法可以描述如下:
规定排列中每一个元素都有一个(可移动的)方向,可以设想在每个元素上有一个箭头,箭头的指向就是元素的(可移动的)方向。如果一个元素的箭头所指的相邻的元素比该元素小时,称该元素处于活动状态。当一个元素在排列的左端,其方向向左,或一个元素在排列的右端,其方向向右,该元素就不处在活动状态,1总是处在不活动的状态。
第一步:初始化n个元素的排列为123……n,并规定其元素的方向都是向左的,元素的方向用一个数组b来表示,当b[i]=0,表示第i个元素的方向向左,当b[i]=1时表示地i个元素的方向向右。
第二步:在排列中找出排列中所有处于活动状态的元素中最大的一个,
第三步:将它与它所指向相邻元素交换。
把排列中大于上面找出的处在活动状态的最大元素大的其他元素的方向倒转。
要得到n个元素的全部排列,可以从原始排列开始,通过上述算法产生新的排列,一直到排列的全体元素都成为不活动的,全部排列就都已生成。
//邻位互换算法
//输入:排列元素个数n
//输出:n个元素的排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *p,int n);
void output(int *p,int n);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n)
{
p=new int[n];
total=0;
perm(p,n);
}
return 0;
}
void perm(int *p,int n)
{
int i,j,x,y,*b=new int[n]; //b是方向数组,b[i]=0向左,b[i]=1向右
for(i=0;i<n;i++) //初始化
p[i]=i+1,b[i]=0;
while(1)
{
output(p,n);
if(!y) //如果全体元素都处在不活动状态
break; //结束
x=y=0;
for(i=0;i<n;i++) //查找活动状态的最大元素x
{
if(i==0 && b[i]==0 || i==n-1 && b[i]==1)
continue;
if((i>0 && b[i]==0 && p[i-1]<p[i]
|| i<n-1 && b[i]==1 && p[i]>p[i+1])
&& x<p[i])
x=p[i],j=i;
}
if(b[j]==0)
swap(p[j-1],p[j]),swap(b[j-1],b[j]); //互换最大元素x与相邻元素
else
swap(p[j],p[j+1]),swap(b[j],b[j+1]);
for(i=0;i<n;i++) //调转大于x的元素的方向
if(p[i]>x)
b[i]=!b[i];
for(i=0;i<n;i++) //检查元素的活动状态
{
if(i==0 && b[i]==1 || i==n-1 && b[i]==0)
y=1;
if(i>0 && b[i]==0 && p[i-1]<p[i]
|| i<n-1 && b[i]==1 && p[i]>p[i+1])
y=1;
}
}
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}