《原创博文,欢迎转载,欢迎交流学习》
归并排序真的挺令人痛苦的,也许是本人天资驽钝,在阅读了大量的解释,说明才有一点点的理解,在这里抛砖引玉,希望大家多多指点,也给刚接触此算法的初学者一点点的启发,如能对你有所帮助,对我则是莫大的鼓励。好了,让我们回归正题:
1,什么是归并排序?
这个问题的答案,百度会告诉我们。归并排序,“归”可以理解为递归,但难点是“并“。
”并“即是‘合并’之意。连起来解释就是,”递分解,归合并“,意思是,在函数往下传递的时候,数组分解,在函数回归之时按顺序合并。
2.算法复杂度
简单点来理解下,是O=nlog2N,是比较优秀的排序算法,相对与冒泡、插入等n2的来说,要快很多倍,
举个例子,数据为10000个,没错是一万个,比较少的数据,
归并排序的次数为10000*log2 10000=132877,13万次多
再看下冒泡:
10000*10000=1亿次
这就是算法的魅力。
3,代码:
此处我使用C语言编写两种归并排序的算法:
第一种是大家非常常见的算法;
C Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | #include #include void merge(int *a,int left,int right,int *temp){ int l=left; //左边数组索引变量 int mid=(left+right)/2; //数组中间值 int r=mid+1; //右边数组索引变量 int i=0; //temp[i],为插入temp的位置 while(l<=mid && r<=right){ //初始循环条件,不确定左右哪个数组先完全进入temp if(a[l]<=a[r]){ //此处为升序,若为降序,修改符号即可 temp[i++]=a[l++];//当左边的值小时,把左边值插入到temp[i]中,并且i和l自增1 }else{ temp[i++]=a[r++];//当右边的值小时,把右边的值插入到temp[i]中,并且i和r自增1 } } //运行至此,可以知道 l<=mid 和 r<=right 肯定有一个不符合了 // 但我们不能确定是哪一个,所以我们采用一种“宁教我负天下人,休教天下人负我”的策略 //也可以理解为‘宁可错杀一千,不可放过一个’ while(l<=mid){ //此处时:万一左边数据没排完,还剩下一些呢 temp[i++]=a[l++]; } while(r<=right){ //此处时:万一右边还剩下一些宝贝呢,把他放进temp【】中吧 temp[i++]=a[r++]; } //到这里,我们可以确定,数据已经完全进入temp了,但是我们要把数据移到a中 for(l=0;l//为甚要用i作为结束标志? 因为只有i可以确定我们到底给temp装了多少个数据,只有i才知道temp到底经历了些什么 a[left+l]=temp[l];//此处意思为 数组a 从left 开始接受数据,直到temp的尽头 } } void merge_sort(int *a,int left,int right,int *temp){ if(left //****************递分解****** int mid=(left+right)/2;//找到中间值 merge_sort(a,left,mid,temp); //将左半边数组排好 merge_sort(a,mid+1,right,temp);//将右边数组排好 //**************归合并********** merge(a,left,right,temp);//将两个排好序的数组合并 } } void sort(int *a,int n){ int *temp=(int*)malloc(sizeof(int)*n);//用来存储合并的数组 merge_sort(a,0,n-1,temp); } int main() { int n,*a,i; //n 为读入一个数组长度,a为动态数组,i为循环数 scanf("%d",&n); //读取一个数字,作为数组长度 a=(int*)malloc(sizeof(int)*n);//申请n个 int 空间 for(i=0;i a[i]=(int)(rand()%100);//给数组a 进行随机赋值[0,99]之间的整数 } //*排序吧,少年! sort(a,n); //*输出吧,少年! for(i=0;i printf("%d\t",a[i]); } //*是不是很厉害,少年! return 0; } |
C Code
第二种是比较拗口的,希望大家仔细写一遍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include #include void merge_sort(int *a,int left,int right,int *temp){ if(left //****************递分解 merge_sort(a,left,mid,temp); //将左半边数组排好 merge_sort(a,mid+1,right,temp);//将右边数组排好 //**************归合并 int l=left; //这里就不解释了 int r=mid+1; int i=left; while(l<=mid || r<=right){ //这里用的是 或 ,意思是 左边和右边都必须出界才结束,就是说左右两边的数据必须全部进入 if(l<=mid &&(r>right || a[l]//这个判断条件是最拗口的一个了; //这样理解:如果我们想把左边的数据放入temp 需要满足怎样的条件 //显然:1.必须没有越界即 l<=mid // 2.要么右边越界(即右边数据全部进入temp,而左边还有剩余)即 r>right,要么左边的数据小于右边 a[l] temp[i++]=a[l++]; }else{ temp[i++]=a[r++]; } } for(l=left;l a[l]=temp[l]; } } } void sort(int *a,int n){ int *temp=(int*)malloc(sizeof(int)*n);//用来存储合并的数组 merge_sort(a,0,n-1,temp); } int main() { int n,*a,i; //n 为读入一个数组长度,a为动态数组,i为循环数 scanf("%d",&n); //读取一个数字,作为数组长度 a=(int*)malloc(sizeof(int)*n);//申请n个 int 空间 for(i=0;i a[i]=(int)(rand()%100);//给数组a 进行随机赋值[0,99]之间的整数 } //*排序吧,少年! sort(a,n); //*输出吧,少年! for(i=0;i printf("%d\t",a[i]); } //*是不是很厉害,少年! return 0; } |