python全排列字典序输出 递归_全排列算法的理解与实现(递归+字典序)

一、全排列的概念

排列:

从n个数中选取m(m<=n)个数按照一定的顺序进行排成一个列,叫作从n个元素中取m个元素的一个排列。不同的顺序是一个不同的排列。从n个元素中取m个元素的所有排列的个数,称为排列数。

全排列:

从n个元素取出n个元素的一个排列,称为一个全排列。全排列的排列数公式为

时间复杂度:

n个数的全排列有n!种,每一个排列都有n个数据,所以输出的时间复杂度为O(n*n!),呈指数级,无法处理大型数据。

二、递归的全排列算法

算法思路:

假设我们要对1,2,3,4四个数进行全排列,过程如下:

(a)首先保持1不变,对2,3,4全排列;

(b)保持2不变,对3,4全排列;

(c)保持3不变,对4全排列,4的排列只有一种。得到1,2,3,4

(d)然后3不能不变了,继续保持2不变,3,4互换得到1,2,4,3

(e)以1,2打头的排列完成,接下来把3换到2的位置,继续(c)、(d)的操作

……

得到1,3,2,4

1,3,4,2

1,4,3,2

1,4,2,3

因此得到以1打头的全部排序,以此类推,得到以2,3,4打头的排序,得到全排序。

将以上过程总结成一个递归算法:

任取一个数打头,对后面n-1个数进行全排序,要求n-1个数的全排序,则要求n-2个数的全排序……直到要求的全排序只有一个数,找到出口。

伪代码:

m到n的全排序

Permutation(m,n){

if:全排列只有一个数,输出排列

else:

for{i=m;i

swap(a[m],a[i]);//把要打头的数放到最开头的位置(即m所在的位置)

Permutation(m+1,n);//递归

swap(a[m],a[i]);//为避免重复排序,每个数打头结束后都恢复初始排序,防止重复的方法很多,不止这一种

}

}

从第m个元素到第n个元素的全排列代码:

void Permutation(int a[],int m,int n){

if(m==n){

cout<

for(int i=1;i

cout<

}

cout<

}

else {

for(int i=m;i

int temp=a[m];

a[m]=a[i];

a[i]=temp;

Permutation(a,m+1,n);

temp=a[m];

a[m]=a[i];

a[i]=temp;

}

}

}

三、字典序

定义:对于一个序列a1,a2,a3,a4,a5....an的两个排列b1,b2,b3,b4,b5...bn和c1,c2,c3,c4,c5...cn, 如果它们的前k项一样,且c(k +1)> b(k+1),则称排列c位于排列b的后面。如1,2,3,4的字典序排在1,2,4,3的前面(k=2),1,3,2,4的字典序在1,2,3,4(k=1)的后面。

显然,对一个无重复元素的集合,它每种排序的字典序位置不同。按字典序进行全排列,使排列变得有序。

e.g.按字典序排列1,2,3的结果:

1,2,3

1,3,2

2,1,3

2,3,1

3,1,2

3,2,1

思路:

该算法的关键在于,找到紧跟在某一个排列后面的字典序。证明过程有点绕,我就讲讲我是如何通俗的理解这个算法的(举的例子可能不太严谨)。

假设有一排列

,显然,若

,则

是它后面的字典序。

,则需不断向前寻找,直到找到

我们可以看到,

是降序排列,是排序中最大的情况(不理解的想象一下用1~9组成的各位不重复的最大数是987654321)。

字典序.jpg

想象有个十进制数19,我们来看一下要找到比它大的数——20,需要做哪些事情:由于个位数是9,已是最大情况,所以我们要将十位稍微调大一点,然后把个位改到最小,便得到紧跟着19后面的数。

因此,仿照上面的例子,我们做以下操作:从am+1,...,an中找到比am大的最小项,和am互换位置,然后再将新的am+1,...,an升序排列(将新的am+1,...,an改到最小,就如十位数20个位的0),这样得到的字典序便是紧挨着排列的后一个字典序。

字典序算法的实现:

bool Next_Permutation(int a[], int n){

int i,m,temp;

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

if(a[i+1] > a[i]) break;

}

if(i < 0) return false;

m = i;

i++;

for(;i

if(a[i] <= a[m]){

i--;

break;

}

else if(i==n-1) break;

}

temp=a[m];

a[m]=a[i];

a[i]=temp;

sort(a+m+1,a+n);

cout<

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值