数据结构排序算法之归并排序

  最近一段时间一直在做项目,没有时间(好吧,我也承认最近有点懒,晚上回去什么都不想干了)。不过最近这两天晚上还是看了一点,就写下来备忘吧。

  排序算法之归并排序:看资料都介绍说这是一种效率非常高的算法,看有的大神进行的测试,在200000个随机数的情况下排序速度比快排还要快。

  其实主要原理也非常简单,看资料中的专业术语都是说采用分治法,分治法的要求是左右两边的数据都要有序(其实当我们左右两边只剩余一个的时候就是有序的)。

  其实所谓的分治法也就是把这堆数据划分开,左边一堆,右边一堆,然后在对左边这堆进行归并排序,也就是把左边这堆在分治划分,也就是在分成左左一堆,左右一堆,然后在往下进行分治划分,直到每个小堆都只有一个数据的时候,划分就完成了,因为只剩下一个数据,所以我们默认这个小堆的数据是有序的(这其实也是废话,就一个数,怎么看当然都是有序的了)。

  光分开肯定不行,因为我们要排序,所以我们还要在合起来,这就是我们的重点了,怎么合起来?这就需要进行数字大小的比较了,我们将两个指针分别放在两个左右小堆的开头处(最左端),然后我们进行两个指针下标对应数据的大小比较,若left[i]<=right[j],则我们将数字小的(即left[i])放在临时数组temp[k]处,然后我们将i,k都进行加1操作,这样的目的是将左边的比较数据往下移动一位,将临时数组的下标往下移动一位,以免被覆盖,这样我们将数据不断进行比较,当有一个小序列已经到头的时候,说明另外一个序列的剩余所有数据都比已经没有的序列的数据大,我们将剩余的数字全部赋值给临时数组。这样就完成了合起来的操作。

  这面就是划分和合并的图示。

  下面就是代码,里面都有注释。

 1 #include <stdio.h>
 2 
 3 void cutTwo(int sourceArr[],int *tempArr[],int start,int end);
 4 void merge(int sourceArr[],int *tempArr[],int start,int mid,int end);
 5 
 6 int main(int argc, char *argv[])
 7 {
 8     int a[8]={
 9         50, 10, 20, 30, 70, 40, 80, 60
10     };
11     int *b[8]={
12     };
13     int i;
14     cutTwo(a,b,0,8);
15     for(i=0;i<8;i++){
16         printf("%d ",a[i]);
17     }
18     return 0;
19 }
20 
21 
22 /*
23 归并排序算法: 
24 */
25 void merge(int sourceArr[],int *tempArr[],int start,int mid,int end){
26     //当前我们有一个源数组,我们在比较时将这个源数组一分为二进行比较归并排序
27     /*
28     因为我们要进行归并排序,所以我们需要两个指针分别指向两个子序列的开始位置,即start指向左边部分的开始位置,
29     mid+1指向右边部分的开始位置,我们还需要一个k的下标,用于存储我们交换过来的数组到临时数组tempArr[] 
30     */ 
31     int i=start;  //定义一个i下标,用于表示左边部分开始的位置下标
32     int j=mid+1;  //定义一个j下标,用于表示右边部分开始的位置下标
33     int k=0;  
34     
35     /*
36     因为我们在比较时是不断的比较,直到一个子序列到达最后,所以我们应该用while循环来做,结束条件:无非就是左边序列到头了
37     或者是右边序列到头了,即:i<=mid&&j<=end  只有在这两个条件都成立的条件下说明两个子序列都没有到头 
38     */
39     while(i<=mid&&j<=end){  //当i=mid+1或者j=end+1时说明子序列中有一个到头了,则跳出循环 
40         if(sourceArr[i]<=sourceArr[j]){  //表示当前i比较小,那么我们就将小的值赋给k 
41             tempArr[k]=sourceArr[i];
42             k=k+1;
43             i=i+1;
44         }
45         else{
46             tempArr[k]=sourceArr[j];
47             k=k+1;
48             j=j+1;            
49         }
50         /*
51         不能将k,i,j的加1操作放在if else判断的外面,因为我们在进行比较的时候,只是将下标所指的数字小的放在左边,将下标所指的数字
52         大的不动,因为我们小的下标加1后还要和刚才下标所指的数字再次进行比较,如果放在外面,那么我们的比较的对象不对了(
53         因为的大的数字的下标加1了,前面的一个数字没有进行比较) 
54         */
55     }
56     
57     /*
58     当有一个子序列到头以后,我们就要将剩余没到头的子序列的剩余元素放到k的右边,因为剩余的肯定是已经有序的,且肯定比已经
59     到头的子序列的全部元素都要大的。 
60     */
61     while(j<=end){  //i==mid+1&&    因为左边序列i跳出循环的条件肯定是当前i=mid+1了,则我们移动右边j的子序列就好了 
62         tempArr[k]=sourceArr[j];
63         k++;
64         j++;
65     }
66     while(i<=mid){  // &&j==end+1   则此时表示当前跳出循环的是j右边的子序列,则我们移动左边的i的子序列 
67         tempArr[k]=sourceArr[i];
68         k++;
69         i++;
70     }
71     int m;
72     for(m=0;m<k;m++){
73         sourceArr[start+m]=tempArr[m];
74     }
75     
76     
77 } 
78 
79 
80 /*
81 上述操作完成仅仅是完成了对一个大的序列中一部分的排列,因为我们的做法是对整个的序列进行二分,二分之后对子序列进行归并排序 
82 */
83 void cutTwo(int sourceArr[],int *tempArr[],int start,int end){    
84     
85     if(start<end){
86         int mid;  //设置一个取中间的变量
87         mid=(start+end)/2;
88         
89         cutTwo(sourceArr,tempArr,start,mid);
90         cutTwo(sourceArr,tempArr,mid+1,end);
91         merge(sourceArr,tempArr,start,mid,end);
92     }
93      
94 }

  该博客系博主自主原创,转载请标明出处哟。

原文地址:https://www.cnblogs.com/WuNaiHuaLuo/p/5446449.html