码迷,mamicode.com
首页 > 编程语言 > 详细

分治法与归并排序

时间:2016-08-12 00:50:15      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:

分治策略

  解决一个给定问题,算法需要一次或多次地递归调用自身来解决相关的子问题,这种算法通常采用分治策略。分治模式在每一层递归上都有三个步骤:

  〉〉分解:将原问题分解成一系列子问题

  〉〉解决:递归地求解各子问题。若子问题足够小,则直接求解

  〉〉合并:将子问题的结果合并成原问题的解。

技术分享

归并排序(合并排序)

  归并排序的关键在于归并两个相邻的子序列,使其变成一个排序好的新序列。如果这个新序列就是原来需要进行排序的数组,那么排序完成。所以,我们需要将原序列递归地分成若干子序列,直道最小的子序列只有一个元素,然后将子序列依次归并,就可以得到排序好的原序列。我们要解决的第一个问题就是:假设有子序列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

 

分治法与归并排序

标签:

原文地址:http://www.cnblogs.com/mrblug/p/5763138.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!