转载请注明出处:http://blog.csdn.net/ns_code/article/details/20306991
归并排序
实现思想
归并的含义很明显就是将两个或者两个以上的有序表组合成一个新的有序表。归并排序中一般所用到的是2-路归并排序,即将含有n个元素的序列看成是n个有序的子序列,每个子序列的长度为1,而后两两合并,得到n/2个长度为2或1的有序子序列,再进行两两合并。。。直到最后由两个有序的子序列合并成为一个长度为n的有序序列。2-路归并的核心操作是将一维数组中前后相邻的两个有序序列归并为一个有序序列。
下面一系列图展示了2-路归并排序的过程:
原始无序序列:
第一次需要对各相邻元素进行两两归并,归并后结果如下:
第三次需要对上图中相邻色块的元素进行两两归并,归并后的结果如下:
接下来便是最后一次两两归并了,归并后便的到了有序的序列,如下:
实现代码:
- /*******************************
- 归并排序
- Author:兰亭风雨 Date:2014-02-28
- Email:zyb_maodun@163.com
- ********************************/
- #include<stdio.h>
- #include<stdlib.h>
- /*
- 将有序的arr[start...mid]和有序的arr[mid+1...end]归并为有序的brr[0...end-start],
- 而后再将brr[0...end-start]复制到arr[start...end],使arr[start...end]有序
- */
- void Merge(int *arr,int *brr,int start,int mid,int end)
- {
- int i = start;
- int j = mid+1;
- int k = 0;
- //比较两个有序序列中的元素,将较小的元素插入到brr中
- while(i<=mid && j<=end)
- {
- if(arr[i]<=arr[j])
- brr[k++] = arr[i++];
- else
- brr[k++] = arr[j++];
- }
- //将arr序列中剩余的元素复制到brr中
- //这两个语句只可能执行其中一个
- while(i<=mid)
- brr[k++] = arr[i++];
- while(j<=end)
- brr[k++] = arr[j++];
- //将brr中的元素复制到arr中,使arr[start...end]有序
- for(i=0;i<k;i++)
- arr[i+start] = brr[i];
- }
- /*
- 借助brr数组对arr[start...end]内的元素进行归并排序
- 归并排序后的顺序为从小到大
- */
- void MSort(int *arr,int *brr,int start,int end)
- {
- if(start < end)
- {
- int mid = (start+end)/2;
- MSort(arr,brr,start,mid); //左边递归排序
- MSort(arr,brr,mid+1,end); //右边递归排序
- Merge(arr,brr,start,mid,end); //左右序列归并
- }
- }
- /*
- 将该排序算法封装起来
- */
- void Merge_Sort(int *arr,int len)
- {
- int *brr = (int *)malloc(len*sizeof(int));
- MSort(arr,brr,0,len-1);
- free(brr);
- brr = 0;
- }
- int main()
- {
- int num;
- printf("请输入排序的元素的个数:");
- scanf("%d",&num);
- int i;
- int *arr = (int *)malloc(num*sizeof(int));
- printf("请依次输入这%d个元素(必须为整数):",num);
- for(i=0;i<num;i++)
- scanf("%d",arr+i);
- printf("归并排序后的顺序:");
- Merge_Sort(arr,num);
- for(i=0;i<num;i++)
- printf("%d ",arr[i]);
- printf("\n");
- free(arr);
- arr = 0;
- return 0;
- }
小总结
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)。(参考:白话经典算法系列之五 归并排序的实现)
归并排序的最好最坏和平均时间复杂度都是O(n*logn),但是需要额外的长度为n的辅助数组(每次递归调用前都会释放上次递归中传入到Merge函数的brr数组),因此空间复杂度为O(n),而不会因为栈的最大深度为O(logn)而积累至O(n*logn)。占用额外空间是归并排序不足的地方,但是它是几个高效排序算法(快速排序、堆排序、希尔排序)中唯一稳定的排序方法。