最近一段时间一直在做项目,没有时间(好吧,我也承认最近有点懒,晚上回去什么都不想干了)。不过最近这两天晚上还是看了一点,就写下来备忘吧。
排序算法之归并排序:看资料都介绍说这是一种效率非常高的算法,看有的大神进行的测试,在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 }
该博客系博主自主原创,转载请标明出处哟。