字典排序问题

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

常见的有四种全排列算法:
(A)字典序法
(B)递增进位制数法
(C)递减进位制数法
(D)邻位对换法

这里着重介绍字典序法

对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。

[例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。

[注意] 一个全排列可看做一个字符串,字符串可有前缀、后缀。

1)生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

[例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。
-----------------------------------------------------------------------
字典序法的一般框架:
1):找最后的可以增加的元素a[i]
2):将a[i]之后的排列最小化

也就是说,只要知道:
1:如何判断某排列是否是满足要求的最大排列(字典序下)
2:如何求满足要求的最小排列(字典序下)
就可以用字典序来解决。

字典序法比递归之类的方法要好许多,但比格雷码序之类的方法就差了不少了。
另外,C++ 中的STL已经用字典序法实现了排列算法:next_permutation( first, last )。也是适合于多重集合排列的。(因为它们判最大排列与求最小排列的方法一样)
-----------------------------------------------------------------------
也可以用于有重复的字符串全排列问题

举个例子方便理解:

1231214
i=4 ,n1=3,n2=2,n3=1,n4=1.n=3+2+1+1=7

排列数=C(3,7)*C(2,4)*C(1,2)*C(1,1)
-----------------------------------------------------------------------
算法思想:

设P是[1,n]的一个全排列。

P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn , j=max{i|PiPj} ,对换Pj,Pk,将Pj+1…Pk-1PjPk+1…Pn翻转, P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个

例子:839647521的下一个排列.

从最右开始,找到第一个比右边小的数字4(因为4<7,而7>5>2>1),再从最右开始,找到4右边比4大的数字5(因为4>2>1而4<5),交换4、5,此时5右边为7421,倒置为1247,即得下一个排列:839651247.用此方法写出全排列的非递归算法如下:

#include
#include
using namespace std;
/*
字典序法生成全排列
*/
string input;
void perm();
int main(){
input="aabcc";//a--z,z最大,input初始值为序列中第一个串(否则先sort一下

)
perm();
return 0;
}
void perm(){
cout<while(1){
int index=-1;
for(int i=input.size()-2;i>=0;i--) // 从最右开始,找到第一个比右边小的数字,赋值给index
{
if(input[i]index=i;
break;
}
}

if(index==-1)break; // 所有排列遍历完,break while

char M='z'; // M为监视哨兼临时变量(for swap)
int C; // C为所找到数字的序号
for(int i=index+1;i<=input.size()-1;i++){ // 再从最右开始,找到input[index]右边比input[index]大的数字
if(input[i]<=input[index])continue;
if(input[i]<=M){
C=i;
M=input[i];
}
}
input[C]=input[index];
input[index]=M; // 交换input[index]和input[C]
int len=input.size()-1-index;
for(int i=1;i<=len/2;i++){ // 将index后面的部分倒置,比如7421,倒置为1247,只需要对称交换即可
char t=input[index+i];
input[index+i]=input[input.size()-i];
input[input.size()-i]=t;
}
cout<}
}


相关文档
最新文档