排列的算法(一)——字典序算法

从n个不同元素中取出n个元素的排列,称为n个不同元素的全排列。

可以证明,n个元素的全排列的总数是n!。

全排列的生成算法就是对于给定的元素集合,用有效的方法将所有可能的全排列无重复无遗漏地列举出来。

n个不同元素的排列都可以与n个自然数12、……,n的排列一一对应,所以,这里就以n个数字的排列为例说明排列的生成算法。全排列的生成算法就是讨论怎样从一个排列生成它一个新的排列的方法。

1.字典序算法

不同的排列,可以从左到右逐个比较对应的元素的顺序来决定他们的先后顺序。设有集合{12、……n-1n}的两个排列 a0a1 ……aj……an-1和 b1b2……bj……bn-1bn,从左端开始,逐个比较它们的对应元素的大小,如果出现对应数字不相同的第一个位置为 j,并且aj<bj,则a0a1 ……aj……an-1就排在b0b1……bj……bn-1的前面。例如对于5个数字的排列1235412345,两个排列的前三个数字相同,出现不同数字的位置是 j=3,而排列12354的第四个数字5在排列12345的第四个数字4之后,所以排列12345在前,排列12354在后。根据这样的规定,5个数字的所有的排列中,第一个排列是12345,最后一个是54321。这种判断排列顺序的方法称为字典序法。

根据字典序法生成所有全排列的算法如下:

设P是集合{12、……n-1n}的一个全排列:P=p1p2……pj-1pjpj+1……pn(1p1、 p2、……、pn≤n-1

第一步:从排列的右端开始,找出第一个比右边数字小的数字的序号j,即j=max{i|pi<pi+1 ,i>j}

第二步:在pj 的右边的数字中,找出所有比pj 大的数字中最小的数字pk,即k=min{i|pi>pj ,i>j}

第三步:交换pi,p

第四步:再将排列右端的递减部分pj+1pj+2……pn倒转,就得到了一个新的排列P'=p1p2……pj-1pjpn……kpj+2pj+1。

例如839647521是数字19的一个排列。从它生成下一个排列的步骤如下: 

自右至左找出排列中第一个比右边数字小的数字4     839647521

在该数字后的数字中找出比4大的数中最小的一个5   839647521

54交换                                    839657421

7421倒转                                     839651247

所以839647521的下一个排列是                    839651247

为了得到集合{12、……n-1n}的全体全全排列,可以从原始排列12……n开始,按照字典序法逐个生成它们的后继排列。当得到的后继排列是最后一个n……21时就结束。

字典序算法的程序代码:

//字典序算法

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

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

#include <iostream>

#include <iomanip>

using namespace std;

 

void dict(int *p,int n);

void output(int *p,int n);

int t;

 

int main(void)

{

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

int n,*p;

    while(cin>>n)

{

p=new int[n];

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

p[i]=i+1;

t=0;

             dict(p,n);

}

return 0;  

}

 

void dict(int *p,int n)

{

int i,j;

while(1)

{

output(p,n);                     

for(i=n-2;i>=0;i--)               

   if(p[i]<p[i+1])

break;

if(i<0)                           

break;                  

for(j=n-1;j>=0;j--)               

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

break;

swap(p[i],p[j]);

j=n-1;

while(i<j)

swap(p[++i],p[j--]);

}

}

 

void output(int *p,int n)

{

     Cout<<setw(5)<<++t<<”:”;

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

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

cout<<endl;

}

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值