全排列:对于给定的集合A{a1,...an},其中的n个元素互不相同,如何输出这n个元素的所有排列。
算法问题:
1、求解1-N的全排列(按字典序输出)
2、给出一个排列,求按字典序的下一个排列
3、给出一个排列,求按字典序的上一个排列;
1解:递归算法求解全排列
这里以A{a,b,c}为例,来说明全排列的生成方法,对于这个集合,其包含3个元素,所有的排列情况有3!=6种,对于每一种排列,其第一个元素有3种选择a,b,c,对于第一个元素为a的排列,其第二个元素有2种选择b,c;第一个元素为b的排列,第二个元素也有2种选择a,c,……,依次类推,我们可以将集合的全排列与一棵多叉树对应。如下图所示
在此树中,每一个从树根到叶子节点的路径,就对应了集合A的一个排列。通过递归算法,可以在多叉树的构建过程,直接生成集合A的全排列,代码如下。
1 #include <stdio.h> 2 #include <stdlib.h> 3 /** 4 交换数组中2元素,传递地址char* 5 */ 6 int swap(char* arr, int i ,int j) 7 { 8 char temp = arr[i]; 9 arr[i] = arr[j]; 10 arr[j] = temp; 11 return 0; 12 } 13 /** 14 1、递归函数 15 2、对数组arr,进行从index开始的全排列; 16 */ 17 int fullPermutation(char* arr, int len, int index) 18 { 19 int i; 20 /** 21 if index == len ; 22 { 23 then 只对一个元素进行全排列,情况只有一种,输出即可 24 printf arr; 25 这也是函数结束条件。 26 } 27 else 28 { 29 对从index位置开始到len进行全排列; 30 位置index出有len-index+1 中取值情况分别是, indwx, indwx+1,....len-1; 31 for i = index --len-1 32 { 33 使:index 取不同值:arr[index] = arr[i], 34 对 index+1---len进行全排列 35 回溯到起点:arr[index] = arr[i] 36 } 37 } 38 函数结束 return; 39 40 */ 41 if(index == len ) 42 { 43 printf("%s\n",arr); 44 } 45 else 46 { 47 for(i = index; i < len ;i++) 48 { 49 swap(arr,index,i); //设index出值为arr[i] 50 fullPermutation(arr,len, index+1); // 对后面的元素全排列 51 swap(arr,index,i); //排列完后回溯的原点 52 } 53 } 54 return 0; 55 } 56 int main() 57 { 58 char arr[] = "1234"; 59 int len = sizeof(arr)/sizeof(char)-1; 60 fullPermutation(arr,len,0); //从位置0出开始全排列 61 return 0; 62 }
2解:
算法思想:
使用字典序输出全排列的思路是,首先输出字典序最小的排列,然后输出字典序次小的排列,……,最后输出字典序最大的排列。这里就涉及到一个问题,对于一个已知排列,如何求出其字典序中的下一个排列。这里给出算法。
- 对于排列a[1...n],找到所有满足a[k]<a[k+1](0<k<n)的k的最大值,如果这样的k不存在,则说明当前排列已经是a的所有排列中字典序最大者,所有排列输出完毕。
- 在a[k+1...n]中,寻找满足这样条件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是说a[l]>a[k],但是小于所有其他大于a[k]的元素。
- 交换a[l]与a[k].
- 对于a[k+1...n],反转该区间内元素的顺序。也就是说a[k+1]与a[n]交换,a[k+2]与a[n-1]交换,……,这样就得到了a[1...n]在字典序中的下一个排列。
1 /** 2 对位置start-end之间元素逆置 3 */ 4 int reverse(char* arr,int start, int end) 5 { 6 int i; 7 int len = start - end +1; 8 if(len > 1) 9 { 10 for(i = 0; i < len/2; i++) 11 { 12 swap(arr, start+i, end-i); //交换元素; 13 } 14 } 15 return 0; 16 } 17 18 int next_permutation(char* arr, int len) 19 { 20 //查找arr中 满足 arr[i] < arr[i+1]的最大i 21 int i, max_i = len -1; 22 int min_i; 23 for(i =0; i< len -1;i++) 24 { 25 if(arr[i] < arr[i+1]) max_i = i; 26 } 27 if(max_i == len -1) printf("%s is last string!\n",arr); 28 for(i = max_i+1, min_i = max_i +1; i<len ;i++) 29 { 30 if(arr[i] > arr[max_i] && arr[i] < arr[min_i]) min_i = i; 31 } 32 if(max_i < len ) 33 { 34 swap(arr, max_i, min_i); 35 reverse(arr, max_i+1, len-1); 36 } 37 printf("%s\n",arr); 38 return 0; 39 }
可以修改的地方:
1 for(i =0; i< len -1;i++) 2 { 3 if(arr[i] < arr[i+1]) max_i = i; 4 } 5 if(max_i == len -1) printf("%s is last string!\n",arr); 6 7 修改为: 8 9 for(i = len -2; i >= 0 ; i--) 10 { 11 if(arr[i] < arr[i+1]) break; 12 } 13 if(i == -1 ) printf("%s is last string \n",arr);
:利用递增i找到最大i
3解:是对问题2的逆序
1 int prev_permutation(char* arr, int len) 2 { 3 int max_i = 0, i; 4 int min_i; 5 for(i =0; i< len -1; i++) 6 { 7 if(arr[i] > arr[i+1]) max_i = i; 8 } 9 if(max_i == len -1) printf("%s is first string\n",arr); 10 for(i = max_i+1, min_i = max_i +1; i<len-1;i++) 11 { 12 if(arr[i] < arr[max_i] && arr[i]>arr[min_i]) min_i = i; 13 } 14 if(max_i < len) 15 { 16 swap(arr, max_i, min_i); 17 reverse(arr,max_i+1, len-1); 18 } 19 printf("%s\n",arr); 20 return 0; 21 }
1 int main() 2 { 3 char arr[] = "3124"; 4 int len = sizeof(arr)/sizeof(char)-1; 5 //fullPermutation(arr,len,0); //从位置0出开始全排列 6 prev_permutation(arr,len); 7 next_permutation(arr,len); 8 return 0; 9 }
补充:
c++ STL中 next_permutation() 与prev_permutation()的使用;
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 int main() 6 { 7 int arr[] = {1,2,3,4}; 8 cout<<"next"<<endl; 9 do 10 { 11 cout<<arr[0]<<arr[1]<<arr[2]<<arr[3]<<endl; 12 }while(next_permutation(arr,arr+4)); 13 cout<<"prev"<<endl; 14 int arr2[] = {4,3,2,1}; 15 do 16 { 17 cout<<arr2[0]<<arr2[1]<<arr2[2]<<arr2[3]<<endl; 18 }while(prev_permutation(arr2,arr2+4)); 19 return 0; 20 }