【排列組合】有序進行全排列的幾種方法
阿新 • • 發佈:2019-01-25
一. 所謂有序的全排列
如輸入不同的數字使其排列後從小到大順序輸出,如: 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! |
假設數串為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("\n");
}
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("\n");
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;
}