全排列和全组合问题
全排列要求排列后的字符个数和原字符串相等,而全组合可以看成求一个子集。
组合问题:
字符串的组合『1 2 3』,从m个字符中挑选长度为n的字符串排列
对于第一个元素,有两种情况:
1 加入这个组合,则需要在剩下的 m-1 挑选 n-1 个字符
2 不加入这个组合,则需要在剩下的 m - 1 挑选 n个字符,然后到第二个参数为0的时候输出
然后求 m = 1 2 3 4 5 6 ... 的字符串排列
在写程序的时候,先考虑第一种情况,再考虑第二种情况,即先push(a[1]),再pop()出来。
还有一种思路是用二进制(0001 ~ 1111),这四位分别是 a b c d
相关问题:输入两个整数n和m,从数列1,2,3...n中随意取几个数,使其和等于m,要求列出所有的组合
全排列:
一. 递归
1、 插入:对于全排列,比如有5个字符abcde,则有5!=120种方法.
假设abcde是一个输入参数,输出的值则是一个全排列集合,有:
f(abcde)=a+f(bcde)
f(bcde)=b+f(cde)
f(cde)=c+f(de)
f(de)={de,ed}
以上就是运算的递归函数,其中f()函数返回的是一集合,而这里的加号,定义为,把一个字符按顺序的插入到一个字符串中。
举个例子:a+bcde,行为就是把a插在每个位置之间,得到的是如下的一个集合:abcde,bacde,bcade,bcdae,bcdea上面是a对单个项进行+操作。 a+f(bcde)则意思是a对一个集合f(bcde)做操作,如上的例子,a对bcde做操作会生成5个项的集合,那么事实上bcde的全排列,有4!就是24,f(bcde)有24个项。所以a+f(bcde)意味着a对于这24个项中的每一个项做操作,每个操作生成5个项的,总的会生成5*24=120个项。
如果对于重复的字符串序列,则只在不同的字符前后插入,对与相同的字符直接skip。
2、交换:就是从第一个数字起每个数分别与后面的数字交换,去重的全排列就是从第一个数字起每个数分别与后面非重复出现的数字交换。
3. 非递归实现
很显然,这6!个数是有大小的,如果按从小到大排列,示意如下:
1 2 3 4 5 6
1 2 3 4 6 5
1 2 3 5 4 6
…………
6 5 4 3 2 1
显然 , 每个数有唯一后继 ! 如果找到一个数没有后继(6 5 4 3 2 1),则停止。
那么问题的重点在于如何判断是否有后继,以及怎样找到后继 ?
是否有后继很好判断,唯一没有后继的只有654321,它的特点是每位的数字比后面的大!
如何找到后继,思路很清楚,即对于一个数,找到一个比它大的并且是最小的数!
从后往前搜索,找到一个极大值点(top) num[top-1] < num[top]。top后面都是num[top-1] > num[top] 递减的,比如926520,top就是第一个2
要使得下个数大于前一个数,在top后面的位置找一个大于num[top-1]的数和num[top-1]交换,但要使得它最小,取最小的但大于num[top-1]的数,即5。
交换之后,top以及其后面的数字还是单调递减的,将其位置对调(top往后,包括top位置),得到最小的数。对换得到956220,将其颠倒得到950226。