1、字典排序
【例】 如何得到346987521的下一个
1,从尾部往前找第一个P(i-1) < P(i)的位置
4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
最终找到6是第一个变小的数字,记录下6的位置i-1
2,从i位置往后找到最后一个大于6的数
4 6 -> 9 -> 8 -> 7 5 2 1
最终找到7的位置,记录位置为m
3,交换位置i-1和m的值
4 7 9 8 6 5 2 1
4,倒序i位置后的所有数据
4 7 1 2 5 6 8 9
则347125689为346987521的下一个排列
代码实现:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 /** 5 * dictionary sequence 6 * count:数组中元素个数 7 */ 8 void dicseq(int *nums,int count) 9 { 10 int i,j,k,L; 11 int tmp; 12 int flag = 0;//标志标明是否还存在下一个排列 13 L = count-1;// 尾节点的下标,特别注意边界值,容易出现段错误 14 //从右往左扫描,是否存在相邻两个数,后一个数大于前一个数,作为判断全排列是否存在的判断 15 for(i = count - 1; i > 0; i--){ 16 //仍然存在排列 17 if(nums[i] > nums[i-1]){ 18 //从右往左找到第一个大于a[i-1]的数,然后对换两数 19 for(j = count-1; j > i-1; j-- ){ 20 if(nums[j] > nums[i-1]){ 21 //对换两个元素 22 tmp = nums[j]; 23 nums[j] = nums[i-1]; 24 nums[i-1] = tmp; 25 26 //翻转后半部分 27 k = i; 28 while(k <= (i + count -1)/2){ 29 tmp = nums[k]; 30 nums[k] = nums[L]; 31 nums[L] = tmp; 32 L--; 33 k++; 34 } 35 break;//从右往左找到第一个就终止 36 } 37 } 38 for(k = 0; k < count; k++){ 39 if(k%10 == 0) 40 printf("\n"); 41 printf("%d\t",nums[k]); 42 if(k == count -1) 43 printf("\n"); 44 45 } 46 flag = 1;//如果从右往左全是递增,那么就不存在其余的全排列,flag就永远是0,不必再递归 47 break;//该函数一次调用找出一个全排列 48 } 49 50 } 51 if(flag) 52 dicseq(nums,count);//递归找出所有的排列,此处存在一个问题,问题规模很大时候会段错误 53 } 54 55 56 /* 57 *the first argument is count of number 58 **/ 59 int main(int argc, char *argv[]){ 60 int count, numb; 61 int i; 62 int *nums; 63 int position; 64 int dest; 65 if( argc < 2){ 66 printf("too few argument\n"); 67 // free(nums); 68 return 1; 69 exit(1); 70 } 71 72 printf("input number is %s\n",argv[1]); 73 count = atoi(argv[1]); 74 nums = (int *)malloc(sizeof(int)*count); 75 printf("please input:\n"); 76 for(i = 0; i < count; i++){ 77 scanf("%d",nums+i); 78 } 79 dicseq(nums,count); 80 }
2、递归
上述的字典排序,可以简单的求出一个全排列的一下个排列,一个的下一个就是这两个全排列之间没有其它的全排列。当可以求出一个全排列的下一个排列,那么使用递归就可以求出所有的全排列。
假设所求全排列的字符串为,abcdefgf 8个字符组成,那么采用递归求其全排列的一种思路就是,首先要分解问题,将问题规模变小,同时问题的形式不变。采用如下两步:
- 原问题问题等价于分别以a、b、c、d、e、f、g为首字母,对剩余其他的7个字母进行全排列的求解。如果abcdefg都是数字的话,这种方法求出来的全排列可能不是字典序的,因为这8个数字可能不是按顺序排列的,如果要按字典序排列,我们可以先对字符串排序,然后进行全排列。
- 当所求全排列的字符只有一个时,不用再次调用函数递归,说明这次全排列已经完成,直接打印即可。
对 问题规模为n的字符串,当选定一个数字作为首字母,那么第一次需要递归函数 n 次,也就是对字符串后面的n-1个字符进行全排列,这用一个循环完成,在循环体中调用自身函数。每次选用一个字符作为已定前缀中的部分时,需要将该字符和后面的字符进行交换一次。
实现代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 /** 4 * nums:数组的首地址,l:数组的首元素的下标,r:数组尾元素的下标 5 * 该函数会打印出nums所有元素的全排列,不会改变nums元素的位置 6 */ 7 void recurseq(int *nums, int l, int r) 8 { 9 int i; 10 int tmp; 11 i = 0; 12 if(l == r){ 13 while(i <= r){ 14 if(i%10 == 0) 15 printf("\n"); 16 printf("%d\t",nums[i]); 17 ++i; 18 if(i == r+1) 19 printf("\n"); 20 } 21 } 22 23 24 if(r > l){ 25 //recurseq(nums, l+1, r); 26 //边界值,i取l-1才能保证nums中的第0号元素为首项,求剩余n-1项的全排列,该值较特殊 27 for(i = l-1; i <r; i++){ 28 printf("l=%d,r=%d,i=%d\n",l,r,i); 29 //交换一次 30 tmp = nums[l]; 31 nums[l] = nums[i+1]; 32 nums[i+1] = tmp; 33 recurseq(nums, l+1, r); 34 //交换回来,恢复nums中数值的顺序 35 nums[i+1] = nums[l]; 36 nums[l] = tmp; 37 } 38 39 } 40 } 41 42 /* 43 *the first argument is count of number 44 **/ 45 int main(int argc, char *argv[]){ 46 int count, numb; 47 int i; 48 int *nums; 49 int position; 50 int dest; 51 if( argc < 2){ 52 printf("too few argument\n"); 53 // free(nums); 54 return 1; 55 exit(1); 56 } 57 58 printf("input number is %s\n",argv[1]); 59 count = atoi(argv[1]); 60 nums = (int *)malloc(sizeof(int)*count); 61 printf("please input:\n"); 62 for(i = 0; i < count; i++){ 63 scanf("%d",nums+i); 64 } 65 recurseq(nums, 0, count -1); 66 }