全排列的算法(四)——n进位法

n进位法

这个算法是将初始排列012……(n-1)作为一个n进制数,然后对它累加1,从而生成全体排列。这个算法比较简单,但是在累加过程中,会出现重复元素,所以需要将有重复元素的排列删除。这是这个算法的缺点。

从一个排列生成一个新的排列的算法如下:

第一步:在排列的第n-1 位累加1

第二步:如果其和大于n-1,就进位,发生连续进位就连续进位直至不再发生进位为止。无进位继续。

第三步:检查排列中有无重复元素,如果有则直接返回第一步;如果无则输出一个排列。

要产生n个元素的全排列,可以从原始排列01……(n-1)开始,对第n-1位累加1,产生一个新的排列后就输出,然后返回重复以上步骤,直到排列的第一个元素超过n-1时停止。

3个数0、12的排列为例:原始排列是1 2 3,从它开始,最后一个元素是3,加上1则有3+1=4,4>3进位,前面一个元素是2,进位后2+1=3,所以新排列是1 3 1,这个排列有重复元素,将它抛弃,继续对最后一个元素加1,……。按照这个访法,顺序产生的排列是:1 2 3,1 3 2,1 3 3,2 1 1,2 1 2,2 1 3,2 2 1,2 2 2,2 2 3,2 3 12 3 2,2 3 3,3 1 1,3 1 23 1 3,3 2 13 2 2,3 2 3,3 3 1,3 3 2,3 3 3,4 1 1,这时第一个元素大于3,程序结束。把其中有重复元素的排列去掉,就得到了三个元素的排列。有下划线的排列中存在重复元素,丢弃,余下的就是全部排列。

需要注意,这里并非严格意义的n进制数,因为每一项进位时是在该项大于n才发生。这样做的目的是为了保证按照给定元素实现排列。

//n位算法

//输入:排列元素个数n

//输出:n个元素的排列

#include<iostream>

#include <iomanip>

using namespace std;

 

void perm(int *p,int n);

void output(int *p,int n);

int repeat(int *p,int n);

int total;

 

int main()

{

    freopen("in.dat","r",stdin);

int n,*p;

    while(cin>>n)

{

p=new int[n];

for(inti0;i<n;i++)            //数组初始化

                   p[i]=i+1;

total=0;

perm(p,n);

}

    return 0;

}

 

void perm(int *p,int n)

{

while(1)

{

if(!repeat(p,n))           //排列中无重复元素

output(p,n);     //输出一个排列 

int i=n-1;               //在排列末尾累加

p[i]++;            

while(p[i]>n && i>0)    //有进位向前进位 

{

p[i--]-=n;

p[i]++;

}

if(p[0]>n)              //排列第一位大于n

return;       //结束 

}

}

void output(int *p,int n)

{

cout<<setw(10)<<++total<<": ";

for(int i=0;i<n;i++)

cout<<setw(3)<<p[i];

cout<<endl;

}

 

int repeat(int *p,int n)

{

for(int i=0;i<n-1;i++)

for(int j=i+1;j<n;j++)

if(p[i]==p[j])

return 1;

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值