全排列问题
跟定一个集合,让你求出它的所有可能的排列组合。这个问题是常见的组合问题,排列的总数目很简单:N!个。但是你能够利用算法将所有可能的排列全部求解出来吗?或者变一下,你能够求解出第x个排列吗?(注:你所有的排列是有序的,暗指是数字序列)。
在组合数学教科书上有很多求这样问题的方法,我简单介绍一下其中的两种。
第一种:
假如给定的集合是ABC,那么我们可以先想一下最终的结果形式可以认为是所有以A开头,以B开头和以C开头的三种。那么继续下去
以A开头的,后面还有BC,接下来的第二个字符是什么呢?同样道理,分为两种,以B开头的和以C开头的。那么结果就是ABC,ACB
以B开头的,后面还有AC,接下来分为两种,分别为以A为第二个字符和以C为第二个字符。那么结果就是BAC和BCA了。
以C开头的。。。结果是CAB和CBA
看出来规律了吗?这是一个递归问题,问题的求解过程就是一个递归树,我们只用把问题的每一种形式全部遍历一遍就可以了。(其实就是让每一字符做一次老大就行了!)
代码如下:
#include <stdio.h> #include <string.h> static void swap(char* pchar1,char* pchar2){ char tmp = *pchar1; *pchar1 = *pchar2; *pchar2 = tmp; } void permutation(char* pChar,int begin,int length){ int i; if(begin==length) { printf("%s\n",pChar); return; } for(i=begin;i<=length;i++){ swap(&pChar[begin],&pChar[i]); permutation(pChar,begin+1,length); swap(&pChar[begin],&pChar[i]); } } int main(){ char arr[] = "abcde"; permutation(arr,0,strlen(arr)-1); }
第二种:字典序法
为什么叫字典序法呢?因为根据这个方法求解出来的所有排列是一个有序的序列。(但前提是对原始集合也有要求,必须是有序的,倒序或者正序都可以)
关于字典序法的原理就不再详述,可以参考组合数学教材。
它的求解过程大致如下:
设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1.....
1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1}
2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者)
3)对换pi,pk
4)再将pj+1......pk-1pkpk+1......pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个排列
举个例子也许你会更明白:
假如给你一个字符集{1,2,3},那么按照字典序法求出的结果是
#include <stdlib.h> #include <stdio.h> static int cmp(const void* pInt1,const void* pInt2){ return (*(int*)pInt1 > *(int*)pInt2); } static void swap(int* pInt1,int* pInt2){ int tmp = *pInt1; *pInt1 = *pInt2; *pInt2 = tmp; } static void reverse(int* pInt1,int* pInt2){ while(pInt1<pInt2){ swap(pInt1,pInt2); ++pInt1; --pInt2; } } static void printList(int* arr,int length){ int i; for(i=0;i<length;i++) printf("%d ",arr[i]); printf("\n"); } void permutation2(int* arr,int length){ int i,j; qsort(arr,length,sizeof(int),cmp); for(i=0;i<length;i++) printf("%d ",arr[i]); printf("\n"); while(1){
//过程一 for(i = length-2;i>=0;i--){ if(arr[i]<arr[i+1]) break; } if(i<0) break;
//过程二 for(j = length-1;j>i;j--) if(arr[j]>arr[i]) break;
//过程三 swap(&arr[i],&arr[j]);
//过程四 reverse(&arr[i+1],&arr[length-1]); printList(arr,length); } } int main(){ int arr[] = {2,1,4,3,5}; permutation2(arr,sizeof(arr)/sizeof(int)); }
该代码写的不是仅仅处理数字问题的情况,严格来讲应该使用STL来处理会更好一些。