算法学习记录-排序——归并排序

归并排序

  利用了完全二叉树的堆排序在效率上提高了不少。但是堆排序主要耗费时间在调整堆上,算法效率也不够稳定。

对于二叉树的应用,还有没有其他方法能够保持算法的效率,也能够使其是一个稳定的算法。(堆排序不够稳定)

具体效率可以查看《算法导论》、《数据结构与算法》(严蔚敏)

 

  想想二叉树的结构,如果我们比较两个数,我们想想将两个树作为叶子,比较结果存放在根中。

如果是四个数呢?

四个数分别为四个叶子,通过两两比较形成一个小集合,然后小集合再比较最后形成最终的结果。

 

这就是归并的思想。开始不理解为什么叫归并,后来看到有人写过:

先递分解,再合成数组

归并算法用了 分治法(Divide and Conquer)的一个非常典型的应用。值得注意的是归并排序是一种稳定的排序方法。(算法比较稳定)

 

理解了,代码就好写了。

两个关键:

分解数组mergeDiv

合并数组mergeIn

看上图分解,我们以左边 9 1为例,最终分解为 9  和 1结点后再开始排序,

mergeDiv(左) 得到的起始序号是0,终止序号是0,得到实际的数==> 9

mergeDiv(右) 得到的起始序号是1,终止序号是1,得到实际的数==> 1

mergeIn(左右)排序,返回==> 1 9

想想在二叉树一章,这有点像后续遍历

 

对于 19 5

mergeDiv(左) 得到的起始序号是0,终止序号是1,得到实际序列==>1 9(之前已经排好了)

mergeDiv(右) 得到的起始序号是2,终止序号是2,得到实际序列==>2

mergeIn(左右)  对上面两个序列 起始0,中间1,终止2 返回  ==>1 2 9 

 

依次类推。。。

 

代码:

 1 void mergeIn(myDataType *ary,int s,int m,int e)
 2 {
 3     int i,j;
 4     int len1 = (m-s)+1;
 5     int len2 = e-m;
 6 
 7     myDataType *list1 = (myDataType *)malloc(sizeof(myDataType)*len1);
 8     myDataType *list2 = (myDataType *)malloc(sizeof(myDataType)*len2);
 9     
10     for (i=0;i<len1;i++)
11     {
12         list1[i] = ary[s+i];
13     }
14 
15     for (i=0;i<len2;i++)
16     {
17         list2[i] = ary[m+1+i];
18     }
19 
20     i=0;
21     j=0;
22     while(i < len1 && j < len2)
23     {
24         if (list1[i] >= list2[j])
25         {
26             ary[s+i+j] = list2[j];
27             j++;
28         }
29         else
30         {
31             ary[s+i+j] = list1[i];
32             i++;
33         }
34     }
35     while(i<len1)
36     {
37         ary[s+i+j] = list1[i];
38         i++;
39     }
40     while(j<len2)
41     {
42         ary[s+i+j] = list2[j];
43         j++;
44     }
45 
46 }
47 
48 void mergeDiv(myDataType *ary,int sIdx,int eIdx)
49 {
50     if (sIdx >= eIdx)
51     {
52         return ;
53     }
54     int mid = (sIdx+eIdx)/2;
55 
56     mergeDiv(ary,sIdx,mid);
57     mergeDiv(ary,mid+1,eIdx);
58     //printf("s=%d,mid=%d,e=%d\n",sIdx,mid,eIdx);
59     mergeIn(ary,sIdx,mid,eIdx);
60 }
61 
62 
63 
64 void mergeSort(myDataType *ary,int len)
65 {
66     mergeDiv(ary,0,len-1);
67 }

按照之前那幅图,绘制了一个函数间调用关系

这里写到了9,后面基本一样。排完左子树,再排右子树,最终返回后,调用mergeIn,完成左右子树排序。

 

完整代码:

  1 #include "stdlib.h"
  2 
  3 typedef int myDataType;
  4 myDataType src_ary[10] = {9,1,5,8,3,7,6,0,2,4};
  5 //myDataType src_ary[10] = {1,2,3,4,5,6,7,8,9,10};
  6 //myDataType src_ary[10] = {10,9,8,7,6,5,4,3,2,1};
  7 void prt_ary(myDataType *ary,int len)
  8 {
  9     int i=0;
 10     while(i < len)
 11     {
 12         printf(" %d ",ary[i++]);
 13     }
 14     printf("\n");
 15 }
 16 
 17 void mergeIn(myDataType *ary,int s,int m,int e)
 18 {
 19     int i,j;
 20     //获取两个序列长度
 21     int len1 = (m-s)+1;
 22     int len2 = e-m;
 23     
 24     //开辟新空间,为排序做准备。
 25     myDataType *list1 = (myDataType *)malloc(sizeof(myDataType)*len1);
 26     myDataType *list2 = (myDataType *)malloc(sizeof(myDataType)*len2);
 27     
 28     //准备两个待比较序列数据
 29     for (i=0;i<len1;i++)
 30     {
 31         list1[i] = ary[s+i];
 32     }
 33 
 34     for (i=0;i<len2;i++)
 35     {
 36         list2[i] = ary[m+1+i];
 37     }
 38 
 39     //比较,因为用来其他空间来存放数据,最后比较后的数据直接给原数据
 40     i=0;
 41     j=0;
 42     while(i < len1 && j < len2)
 43     {
 44         if (list1[i] >= list2[j])
 45         {
 46             ary[s+i+j] = list2[j];
 47             j++;
 48         }
 49         else
 50         {
 51             ary[s+i+j] = list1[i];
 52             i++;
 53         }
 54     }
 55     while(i<len1)
 56     {
 57         ary[s+i+j] = list1[i];
 58         i++;
 59     }
 60     while(j<len2)
 61     {
 62         ary[s+i+j] = list2[j];
 63         j++;
 64     }
 65 
 66 }
 67 
 68 void mergeDiv(myDataType *ary,int sIdx,int eIdx)
 69 {
 70     if (sIdx >= eIdx)
 71     {
 72         return ;
 73     }
 74     int mid = (sIdx+eIdx)/2;
 75 
 76     mergeDiv(ary,sIdx,mid);
 77     mergeDiv(ary,mid+1,eIdx);
 78     printf("s=%d,mid=%d,e=%d\n",sIdx,mid,eIdx);
 79     mergeIn(ary,sIdx,mid,eIdx);
 80 }
 81 
 82 
 83 
 84 void mergeSort(myDataType *ary,int len)
 85 {
 86     mergeDiv(ary,0,len-1);
 87 }
 88 
 89 int _tmain(int argc, _TCHAR* argv[])
 90 {
 91     printf("before sort:\n");
 92     prt_ary(src_ary,10);
 93 
 94     //bubble_sort(src_ary,10);
 95     //bubble_sort_modify1(src_ary,10);
 96     //bubble_sort_opt(src_ary,10);
 97     //selectionSort(src_ary,10);
 98     //insertionSort(src_ary,10);
 99     //shellSort(src_ary,10);
100     //heapSort(src_ary,10);
101     mergeSort(src_ary,10);
102     printf("after sort:\n");
103     prt_ary(src_ary,10);
104 
105 
106 
107     getchar();
108     return 0;
109 }


归并排序用了其他内存空间,是一个耗费空间排序,但是其稳定性高。

 

结果:

 

中间的就是 每次访问时候的序号。

 

 

 

posted @ 2013-12-07 11:40  sjdang  阅读(330)  评论(0编辑  收藏  举报