程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 分治法與歸並排序,分治法歸並排序

分治法與歸並排序,分治法歸並排序

編輯:關於C語言

分治法與歸並排序,分治法歸並排序


本文部分內容參考了《算法導論》

分治策略

  解決一個給定問題,算法需要一次或多次地遞歸調用自身來解決相關的子問題,這種算法通常采用分治策略。分治模式在每一層遞歸上都有三個步驟:

  〉〉分解:將原問題分解成一系列子問題

  〉〉解決:遞歸地求解各子問題。若子問題足夠小,則直接求解

  〉〉合並:將子問題的結果合並成原問題的解。

歸並排序(合並排序)

  歸並排序的關鍵在於歸並兩個相鄰的子序列,使其變成一個排序好的新序列。如果這個新序列就是原來需要進行排序的數組,那麼排序完成。所以,我們需要將原序列遞歸地分成若干子序列,直道最小的子序列只有一個元素,然後將子序列依次歸並,就可以得到排序好的原序列。我們要解決的第一個問題就是:假設有子序列A[p...q]和子序列[q + 1...r]是已經排序好的子序列,如何按順序將它們歸並到一個子序列中去呢?

歸並兩個子序列

  我們可以用撲克牌做模擬。將兩堆數量相近的撲克牌按順序疊好,最小的牌放在最上面,牌面朝上。

  〉〉第一步:拿出兩張中較小的一張,放在桌上。

  〉〉第二步:分別比較所拿的牌和兩個堆上面的牌,重復第一步,跟在前一張牌的後面。直到一個堆拿完。

  〉〉第三步:將剩余的堆從上往下一次放在桌上,跟在前一張牌的後面。

  由此可見,按問題要求(加橙色的),我們可以設計如下代碼:

void merge(int ar[], int p, int q, int r, int temp[]){
        //將ar[p...q]和ar[q+1...r]合並到temp[p...r],temp由外部分配內存
    int i = p, j = q + 1, k = 0;
    while(i <= q && j <= r){
        if(ar[i] < ar[j])
            temp[k++] = ar[i++];
        else
            temp[k++] = ar[j++];
    }
    while(i <= q) //如果ar[p..q]有剩
        temp[k++] = ar[i++];
    while(j <= r) //如果ar[q+1..r]有剩
        temp[k++] = ar[j++];

    for(k = 0; k <= (r - p); k++) //將合並後的子序列賦值給原序列ar[p...r]
        ar[p + k] = temp[k];
}

  對於歸並排序,我們要解決的第二個問題就是:原序列如何成為有序序列?

利用分治策略使原序列有序

  我們可以通過將原序列二分為兩個子序列,再將兩個子序列二分為另外的四個子序列,直到不能再分解為止。然後分別合並相鄰的兩個子序列,直到不能合並為止。這裡引用網絡上的一張圖修改後來說明這個原理。

 

   前面提到過,解決一個給定問題,算法需要一次或多次地遞歸調用自身來解決相關的子問題,這種算法通常采用分治策略。所以,我們可以利用分治策略通過通過遞歸調用一個函數來解決。我們可以這樣寫代碼:

void mergesort(int ar[], int head, int end, int temp[]){
    if(head < end){ //條件:直到不能分割為止
        int middle = (head + end) / 2; //找到二分原序列的點
        mergesort(ar, head, middle, temp); //左子序列排序
        mergesort(ar, middle + 1, end, temp); //右子序列排序
        merge(ar, head, middle, end, temp); //合並這兩個子序列
    }
}

   分析上面的代碼,我們可以將上圖標上執行順序(建議在新窗口打開下圖),來驗證該算法的正確性。同時,你也可以通過類似“循環不變式”的方法證明:

完整代碼

  到這裡,歸並排序的所有問題都解決完了。我們給出完整的不帶注釋(分配內存後的空注釋是標志,提醒一定要及時釋放內存)的代碼。

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 #include <time.h> 5 6 void mergesort(int [], int, int, int []); 7 void merge(int [], int, int, int, int[]); 8 9 int main(int argc, char * argv[]){ 10 int * ar, * temp; 11 int n = 10, i; 12 13 if( !(ar = (int *)malloc( (size_t)n * sizeof(int) ) ) )// 14 exit(EXIT_FAILURE); 15 if( !(temp = (int *)malloc( (size_t)n * sizeof(int) ) ) )// 16 exit(EXIT_FAILURE); 17 18 srand( (unsigned int)time(NULL) ); 19 for(i = 0; i < n; i++){ 20 ar[i] = rand() % 1000; 21 printf("%-4d",ar[i]); 22 } 23 24 mergesort(ar, 0, n - 1, temp); 25 26 printf("\n歸並排序後:\n"); 27 for(i = 0; i < n; i++) 28 printf("%-4d",ar[i]); 29 30 free(temp); 31 free(ar); 32 33 _getch(); 34 return 0; 35 } 36 37 void mergesort(int ar[], int head, int end, int temp[]){ 38 if(head < end){ 39 int middle = (head + end) / 2; 40 mergesort(ar, head, middle, temp); 41 mergesort(ar, middle + 1, end, temp); 42 merge(ar, head, middle, end, temp); 43 } 44 } 45 46 void merge(int ar[], int p, int q, int r, int temp[]){ 47 int i = p, j = q + 1, k = 0; 48 while(i <= q && j <= r){ 49 if(ar[i] < ar[j]) 50 temp[k++] = ar[i++]; 51 else 52 temp[k++] = ar[j++]; 53 } 54 while(i <= q) 55 temp[k++] = ar[i++]; 56 while(j <= r) 57 temp[k++] = ar[j++]; 58 59 for(k = 0; k <= (r - p); k++) 60 ar[p + k] = temp[k]; 61 } MergeSort

   下面這個是帶有入口函數的,只要在main()函數中把數組名和大小傳遞給mergesort()函數就行了。

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 #include <time.h> 5 6 void mergesort(float *, int); 7 void _mergesort(float *, int, int, float *); 8 void merge(float *, int, int, int, float *); 9 10 int main(int argc, char * argv[]){ 11 float * ar; 12 int n = 10, i; 13 14 if( !(ar = (float *)malloc( (size_t)n * sizeof(float) ) ) )// 15 exit(EXIT_FAILURE); 16 17 srand( (unsigned int)time(NULL) ); 18 for(i = 0; i < n; i++){ 19 ar[i] = (float)((rand() % 499)) / (float)((rand() % 11 + 1)); 20 printf("%-6.3g",ar[i]); 21 } 22 23 mergesort(ar, n); 24 25 printf("\n歸並排序後:\n"); 26 for(i = 0; i < n; i++) 27 printf("%-6.3g",ar[i]); 28 29 free(ar); 30 31 _getch(); 32 return 0; 33 } 34 35 void mergesort(float * ar, int size){ 36 if(size > 0){ 37 float * temp; 38 if(!(temp = (float *)malloc( (size_t)size * sizeof(float) ) ) ) // 39 exit(EXIT_FAILURE); 40 _mergesort(ar, 0, size - 1, temp); 41 free(temp); 42 } 43 } 44 45 void _mergesort(float * ar, int head, int end, float * temp){ 46 if(head < end){ 47 _mergesort(ar, head, (head + end) / 2, temp); 48 _mergesort(ar, (head + end) / 2 + 1, end, temp); 49 merge(ar, head, (head + end) / 2, end, temp); 50 } 51 } 52 53 void merge(float * ar, int p, int q, int r, float * temp){ 54 int i = p, j = q + 1, k = 0; 55 while(i <= q && j <= r){ 56 if( ar[i] < ar[j] ) 57 temp[k++] = ar[i++]; 58 else 59 temp[k++] = ar[j++]; 60 } 61 while(i <= q) 62 temp[k++] = ar[i++]; 63 while(j <= r) 64 temp[k++] = ar[j++]; 65 66 for(k = 0; k <= (r - p); k++) 67 ar[p + k] = temp[k]; 68 } 更好的MergeSort

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved