全排列问题

跟定一个集合,让你求出它的所有可能的排列组合。这个问题是常见的组合问题,排列的总数目很简单: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},那么按照字典序法求出的结果是

123,132,213,231,312,321
设有排列2763541,按照字典序排列,它的下一个排列是谁?
(1) 2763541  [找最后一个正序35]
(2) 2763541 [找3后面比3大的最后一个数4]
(3) 2764531 [交换3,4的位置]
(4) 2764135 [把4后面的531反序排列为135 
这样就得到了最终的下一个序列,你可以看出来这个数字是比2763541的所有可能组合中比它大的最小的数字,这也就是字典序法的原理。
代码如下:
#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来处理会更好一些。

 
posted @ 2012-07-19 11:00  f_x_p  阅读(2495)  评论(0编辑  收藏  举报