zoukankan      html  css  js  c++  java
  • 【排列组合】有序进行全排列的几种方法

    一. 所谓有序的全排列

    如输入不同的数字使其排列后从小到大顺序输出,如:
    123
    则可以输出如下组合
    1 2 3
    1 3 2
    2 1 3
    2 3 1
    3 1 2
    3 2 1
    共六种情况
    后面将问题简化一下,输入1-9代表不同的数字1~9,如输入4则对1234进行排序。

    二. 有序全排列的思路

    1. 数学法
    观察a1,a2,a3,...,an的排列情况,假设我需要第k个排列结果。(注意:a1<a2<a3<...<an)
    如果第一个元素a1不需要交换,一共有(n-1)!种排列,当k>(n-1)!时,第一个元素必定不是a1了,那会是哪个元素呢?
    以这个元素为开头的排列 排列序号
    a1 1~(n-1)!
    a2 (n-1)!+1~2*(n-1)!
    an (n-1)*(n-1)!+1~n!
    那么一直求第k个序号的排列,就可以确定第一个元素是多少;
    确定第一个元素后,下一层每个元素的范围则缩小到(n-2)!,而问题与上述无异,也就是说,可以利用这种规律,逐个元素求出这个排列。

    假设数串为1234,总共有4!=24种排列情况,求第15种排列结果。
    第一个元素则为[(15-1)/3!]+1=3,取1234中的第三个作为a1,k=15-6*2=3,未用元素为124
    第二个元素则为[(3-1)/2!]+1=2,取124中的第二个作为a2,k=3-2*1=1,未用元素为14
    由于为1,所以剩余的则为14顺序
    结果为3214
    我们按顺序写一下,确保准确无误
    1234 1243 1324 1342 1423 1432 2134 2143 2314 2341 2413 2431 3124 3142 3214 3241 3412 3421 4123 4132 4213 4231 4312 4321
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

    那么我们用代码把它实现即可(当然,你的实现方法可能更好,因为是很久前写的,将就着看吧)
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int find_kth_element(int n, int kth, int **ans){
    	int *used;
    	int jiecheng, k, temp, ntemp=n, kk;
    
    	for(k=1, jiecheng=1; k<n-1; k++){
    		jiecheng *= (k+1);
    	}
    	used = (int*)malloc(sizeof(int)*n);
    	*ans = (int*)malloc(sizeof(int)*n);
    	memset(used, 0, sizeof(int)*n);
    	
    	for(k=0; k<n; k++){
    		*(*ans+k) = k;
    	}
    
    	if(kth>jiecheng*n)
    		return -1;
    	n--; k=0; kth--;
    	while(1){
    		if(kth==0){
    			for(; k<ntemp; k++){
    				kk=0;
    				while(used[kk++]!=0);
    				used[kk-1] = 1;
    				*(*ans+k) = kk-1;
    			}
    			break;
    		}
    		temp=kth/jiecheng;
    		kk=0;
    		do{
    			while(used[kk++]!=0);
    			temp--;
    		}while(temp>=0);
    		*(*ans+k) = kk-1;
    		used[kk-1]=1;
    		kth = kth%jiecheng;
    		jiecheng = jiecheng/n;
    		n--; k++;
    	}
    	//free(used);
    	return 0;
    }
    
    int main(void){
    	int n, kth;
    	int *num, k, *ans=NULL;
    
    	printf("n="); scanf("%d", &n);
    	printf("kth="); scanf("%d", &kth);
    	
    	num = (int*)malloc(sizeof(int)*n);
    	for(k=0; k<n; k++){
    		*(num+k) = k+1;
    	}
    
    	find_kth_element(n, kth, &ans);
    	for(k=0; k<n; k++){
    		printf("%d", *(num+*(ans+k)));
    	}
    
    	system("pause");
    	return 0;
    }

    2. 逐层排列法
    思路:每层元素交换互不干扰的原则,每层第一个元素与第一个元素、第二个元素。。。交换元素,并保存该层交换结果,直至交换到第n个元素。
    以1234为例
    第一层为1234  位置1与位置1的元素交换(后面简写成1<->1),得到1234,进入第二层(234), 1<->1,得到234,进入第三层(23),1<->1,得到34, 进入第四层(4),1<->1,得到4,所以,第一个排列结果为1234;
    第四层继续交换,1<->2,超出元素个数,返回第三层(34),1<->2,得到43,同样进入第四层(3),。。。得到第二个排列结果为1243;
    第四层继续交换,1<->2,超出元素个数,返回第三层(43),1<->3,超出元素个数,返回第二层(234),1<->2,得到324,然后进入第三层(24)。。。得到第三个排列结果1324,紧接着是1342;
    在退回第二层(324),当时记录结果是324,那么现在要1<->3,得到423,再进入第三层(23),依次得到1423  1432;
    退回第二层,此时1<->4,超出元素个数,返回第一层(1234),1<->2,得到2134,进入第二层(134),从1<->1开始排列,依次得到2134  2143  2314  2341  2413  2431;
    返回第一层(2134),1<->3,得到3124,然后依次得到2134  2143  2314  2341  2413  2431
    返回第一层(3124),1<->4,得到4123,然后依次得到4123  4132  4213  4231  4312  4321
    这样得到了全排列。
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    static int sts=0;
    void dis_num(int *num, int n){
    	int k;
    	
    	printf("%d: ", sts);
    	for(k=0; k<n; k++){
    		printf("%d ", *(num+k));
    	}
    	printf("
    ");
    }
    
    void quanpailie(int *num, int k, int n){ //按层进行全排列,一层交换方法 1 2 3 4->2 1 3 4->3 1 2 4->4 1 2 3 第一个元素逐个向后交换,然后再对第二位之后的数据进行全排列
    	//num:原数字序列,k:从第几个元素开始对调,n:数字序列数字个数
    	int *newnum;
    	int kk, temp;
    
    	if(k>=n){
    		++sts;
    		dis_num(num, n);
    		return;
    	}
    	newnum = (int*)malloc(sizeof(int)*n);
    	memcpy(newnum, num, sizeof(int)*n);  //不恢复序列就是从小到大,一层交换例子 1 2 3 4->2 1 3 4->3 1 2 4->4 1 2 3
    //-------->问题代码,全排列结果非从小到大
    	for(kk=k; kk<n; kk++){
    		//memcpy(newnum, num, sizeof(int)*n);  //恢复序列,交换下一个元素,这个放的位置不一样,结果就不同
    		temp = newnum[k];
    		newnum[k] = newnum[kk];
    		newnum[kk] = temp;
    		quanpailie(newnum, k+1, n);   //交换后,元素k后面的元素进行全排列
    	}
    }
    
    int main(void){
    	int n, k;
    	int *num;
    
    	printf("n="); scanf("%d", &n);
    	if(n<1&&n>9)
    		return 0;
    	num = (int*)malloc(sizeof(int)*n);
    	for(k=0; k<n; k++){
    		*(num+k) = k+1;
    	}
    
    	quanpailie(num, 0, n);
    
    	system("pause");
    	return 0;
    }

    3.局部排序
    规律:
    在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,
    即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。
    比如:12345开始组合排列,12354,12435,...,12543,在12位置不变情况下,最大组合情况为12543,然后,比2略大的数必定在从后向前搜索到的第一个数,所以改为13542,此时需要从最小组合开始,对542进行排序,得到结果13245;
    又当13位置不变,其最大为13542,向后搜索到比3大的为4,则改为14532,然后对4之后的数排序,的14235. 排序复杂度决定这个算法复杂度。
    #include <stdio.h>
    #include <stdlib.h>
    
    int find(int *d, int n, int *i, int *ii, int *j){
    	int k;
    	for(k=n-1; k>=1; k--){
    		if(*(d+k-1)<*(d+k)){
    			*i=k-1;
    			*ii=k;
    			break;
    		}
    	}
    	if(k==0)
    		return -1;
    	for(k=n-1; k>=0; k--){
    		if(*(d+*i)<*(d+k)){
    			*j = k;
    			break;
    		}
    	}
    	return 0;
    }
    
    int partition(int *d, int low, int high){
    	int temp;
    
    	while(low<high){
    		while(low<high&&*(d+low)<=*(d+high)) high--;
    		temp = *(d+low);
    		*(d+low) = *(d+high);
    		*(d+high) = temp;
    		while(low<high&&*(d+high)>=*(d+low)) low++;
    		temp = *(d+low);
    		*(d+low) = *(d+high);
    		*(d+high) = temp;
    	}
    	return low;
    }
    
    void quick_sort(int *d, int low, int high){
    	int mid;
    	if(low<high){
    		mid = partition(d, low, high);
    		quick_sort(d, low, mid-1);
    		quick_sort(d, mid+1, high);
    	}
    }
    
    void display_full_array(int *d, int n){
    	int k;
    	int i, ii, j;
    	int temp;
    
    	while(1){
    		for(k=0; k<n; k++){
    			printf("%d", *(d+k));
    		}
    		printf("
    ");
    		if(find(d, n, &i, &ii, &j)==-1)
    			break;
    		temp = *(d+i);
    		*(d+i) = *(d+j);
    		*(d+j) = temp;
    		quick_sort(d, ii, n-1);
    	}
    }
    
    int main(void){
    	int n;
    	int *d, k;
    
    	scanf("%d", &n);
    	if(n>0&&n<10){
    		d = (int*)malloc(sizeof(int)*n);
    		for(k=0; k<n; k++){
    			*(d+k) = k+1;
    		}
    	}
    	else
    		return 0;
    
    	display_full_array(d, n);
    
    	system("pause");
    	return 0;
    }



  • 相关阅读:
    用python2和python3伪装浏览器爬取网页
    详解python2 和 python3的区别[附实例]
    两种判断(抓取)网页编码的方法【python版】
    python用两种方法实现url短连接
    2013年1月编程语言排行榜榜单: ObjectiveC继续增长
    年初给力!教你自己动手做手机APP应用!!
    [原创]用python求第1000个质数的值
    linux下如何安装配置redis及主从配置
    第四次博客作业结对项目
    2sat的一些总结
  • 原文地址:https://www.cnblogs.com/xhyzjiji/p/6159371.html
Copyright © 2011-2022 走看看