全排列以及字典序

来源:互联网 发布:ios9.3.3数据精灵 编辑:程序博客网 时间:2024/05/19 10:33

http://blog.csdn.net/morewindows/article/details/7370155/


1. 具有重复字符的全排列:

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。

换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。

这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。用编程的话描述就是第i个数与第j个数交换时,要求[i,j)中没有与第j个数相等的数。

比如对于abcbd来说:

当a与第一个b交换之后:bacbd,下一步递归就是对acbd进行全排列;

如果a与第二个b也进行交换:bbcad,下一步递归就是对bcad进行全排列,这个全偶爱列和上面的acbd的全排列的结果是一样的,因此最后产生的整体的全排列也是一样的,因为起始字符都是b,因此去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换!!!

2.非递归的全排列

要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。

如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。

对于像"4321"这种已经是最“大”的排列,采用STL中的处理方法,将字符串整个颠倒得到最“小”的排列"1234"并返回false。

这样,只要一个循环再加上计算字符串下一个排列的函数就可以轻松的实现非递归的全排列算法。按上面思路并参考STL中的实现源码,不难写成一份质量较高的代码。值得注意的是在循环前要对字符串排序下,可以自己写快速排序的代码(请参阅《白话经典算法之六 快速排序 快速搞定》),也可以直接使用VC库中的快速排序函数(请参阅《使用VC库函数中的快速排序函数》)。

上面这种适用于按字典序的全排列!!!!


3. 递归的全排列

为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑213和321这二个数是如何得出的。显然这二个都是123中的1与后面两数交换得到的。然后可以将123的第二个数和每三个数交换得到132。同理可以根据213和321来得231和312。因此可以知道——全排列就是从第一个数字起每个数分别与它后面的数字交换。



包含大小写字母的字符数组按照字典序排序:首先说,如果有大小写混合,按字典顺序是不区分大小写的,而再C语言中大小写是按ASCII码值区分的,小写字母的值大于大写字母的,例如大写A是65,小写a是97。所以应该统一,将大写转换为对应的小写字母输出。这其实是一个排序的算法。
大小写转换的方式:
小写转大写:
f = (f < 'a' && f >= 'A') ? f : (char) (f - 'a' + 'A');

大小写转换的方法

A ascii 为 65, a 为 97

A 65 0100,0001 
a 97 0110,0001–>a的第6位转为0,就能变为A

ch = ch | 32 –> 大写转小写,把第6位, 置为1 
ch = ch & 0xdf –> 小写转大写,把第6位, 置为0

int mark = 0xffdf; // 1...1,1101,1111,转换第6位// 这儿小写转大写// 注意空格的情况f = (char) (f & mark);s = (char) (s & mark);



原创粉丝点击