归并排序(Merge Sort)
2017-01-17 09:21
525 查看
归并排序(Merge Sort)
归并排序Merge Sort基本思想
排序流程
算法实现
算法分析
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
1. 基本思想
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。2.排序流程
设两个有序的子序列放在同一序列中相邻的位置上:A[low..m]和A[m + 1..high],先将它们合并到一个局部的暂存序列 temp 中,待合并完成后将 temp 复制回 array[low..high]中,从而完成排序。合并过程:(1)设置 i,j 和 p 三个指针,其初值分别指向这三个记录区的起始位置。
(2)依次比较 array[i] 和 array[j] 的关键字,取关键字较小(或较大)的记录复制到 temp[p] 中
(3)将被复制记录的指针 i 或 j 加 1,以及指向复制位置的指针 p加 1。
(4)重复这一过程直至两个输入的子序列有一个已全部复制完毕(不妨称其为空),此时将另一非空的子序列中剩余记录依次复制到 array 中即可。
归并排序分为两大步骤:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
3. 算法实现
递归或者循环实现分解合并。代码
#include <stdio.h> #define N 9 int arr1 ={4,7,1,2,9,5,3,8,6}; int arr2 ={4,7,1,2,9,5,3,8,6}; int step=0; int dep=0; /* 打印数组 */ void printArr(int array[], int start, int mid, int end) { int i; printf("\t{["); for(i=start;i<=end;i++) { printf("%d",array[i]); if(i!=mid && i!=end) printf(", "); if(i==mid) { if(mid!=end) { printf("],["); } } } printf("]}\n"); } /* * 函数功能:两个子序列合并为一个 [start,mid] [mid+1,end] * source:原序列;temp:中间序列(合并存放);start:原序列起始位置;mid:原序列中间位置;end:原序列结束位置 */ void merge(int source[],int temp[], int start, int mid, int end) { if(start > mid || mid > end) /* 大小关系不正确,退出 */ return; printf("Before Merge:"); printArr(source,start,mid,end); int i = start; //子序列1起始 int j=mid+1; //子序列2起始 int k = start; //中间序列起始 while(i <= mid && j <= end) //终止条件:其中任意一个序列合并完(为空) { /* 比较两个序列中的值,将较小的值放入中间序列 */ if(source[i] > source[j]) temp[k++] = source[j++]; else temp[k++] = source[i++]; } /* 序列1未完,将剩余元素放入中间序列之后 */ while(i <= mid) temp[k++] = source[i++]; /* 序列2未完,将剩余元素放入中间序列之后 */ while(j <= end) temp[k++] = source[j++]; /* 将合并完成的序列放回原序列 */ for(i=start; i<=end; i++) source[i] = temp[i]; printf("After Merge:"); printArr(source,start,end,end); } /* 归并排序--内部使用递归分解 */ void merge_sort(int source[], int temp[], int start, int end) { int mid; /* 序列从中间位置分割为两部分 */ dep++; if(start < end) /* 此时单个元素,有序 */ { mid = (start + end) / 2; merge_sort(source, temp, start, mid); /* 前半部分 */ merge_sort(source, temp, mid+1, end); /* 后半部分 */ merge(source, temp, start, mid, end); /* 合并两部分 */ } } /* 归并排序--for循环分解 */ void merge_sort_for(int source[], int temp[], int len) { int l=1; while(l<len) { int s=l; //刚开始单个序列有序,开始合并 int i=0; l = s*2; //从起始位置,每隔l进行一次合并[i,i+s-1,i+l-1] while(i+l<len) { merge(source, temp, i, i+s-1, i+l-1); //等长序列合并 i+=l; } if(i+s<len) { merge(source, temp, i, i+s-1, len-1); //在序列长度范围内最后一次合并有可能出现不等长的情况 } } } void main() { int temp ={0}; printf("Before...\n"); printArr(arr1,0,N-1,N-1); printf("Sorting...\n"); merge_sort(arr1,temp,0,N-1); printf("Sorted.\n"); printf("Before...\n"); printArr(arr2,0,N-1,N-1); printf("Sorting by for...\n"); merge_sort_for(arr2,temp,N); printf("Sorted.\n"); }
结果
Before... {[4, 7, 1, 2, 9, 5, 3, 8, 6]} Sorting... Before Merge: {[4],[7]} After Merge: {[4, 7]} Before Merge: {[4, 7],[1]} After Merge: {[1, 4, 7]} Before Merge: {[2],[9]} After Merge: {[2, 9]} Before Merge: {[1, 4, 7],[2, 9]} After Merge: {[1, 2, 4, 7, 9]} Before Merge: {[5],[3]} After Merge: {[3, 5]} Before Merge: {[8],[6]} After Merge: {[6, 8]} Before Merge: {[3, 5],[6, 8]} After Merge: {[3, 5, 6, 8]} Before Merge: {[1, 2, 4, 7, 9],[3, 5, 6, 8]} After Merge: {[1, 2, 3, 4, 5, 6, 7, 8, 9]} Sorted. Before... {[4, 7, 1, 2, 9, 5, 3, 8, 6]} Sorting by for... Before Merge: {[4],[7]} After Merge: {[4, 7]} Before Merge: {[1],[2]} After Merge: {[1, 2]} Before Merge: {[9],[5]} After Merge: {[5, 9]} Before Merge: {[3],[8]} After Merge: {[3, 8]} Before Merge: {[4, 7],[1, 2]} After Merge: {[1, 2, 4, 7]} Before Merge: {[5, 9],[3, 8]} After Merge: {[3, 5, 8, 9]} Before Merge: {[1, 2, 4, 7],[3, 5, 8, 9]} After Merge: {[1, 2, 3, 4, 5, 7, 8, 9]} Before Merge: {[1, 2, 3, 4, 5, 7, 8, 9],[6]} After Merge: {[1, 2, 3, 4, 5, 6, 7, 8, 9]} Sorted.
4. 算法分析
时间复杂度从递归版本看,时间复杂度主要由merge_sort和merge函数决定,merge中有两个长度为n的循环,复杂度为O(n),merge中的递归每次将序列分为两部分,递归深度为logn,所以其时间复杂度为O(nlogn);从循环版本看,时间复杂度主要由两层循环决定,其中外层循环每次翻倍,需要循环logn次,内层循环将序列中所有元素归并一次,复杂度为O(n),所以其时间复杂度为O(nlogn);从归并原理看,归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的可以得出它的时间复杂度是O(nlogn)。
最好情况下、最差情况下和平均情况下的时间复杂度都为 O(nlogn)。
空间复杂度
算法处理过程中,需要一个大小为n的临时存储空间用以保存合并序列,所以其中间复杂度为O(n)。
稳定性
在归并排序中,相等的元素的顺序不会改变,所以它是稳定的算法。
总结
归并排序的最好、最坏和平均时间复杂度都是O(nlogn),而空间复杂度是O(n),同时归并排序是稳定的,但是这种算法很消耗空间,一般来说在内部排序不会用这种方法,而是用快速排序;外部排序才会考虑到使用这种方法。
相关文章推荐
- Java基础知识强化55:经典排序之归并排序(MergeSort)
- 第三周作业——冒泡排序(BubbleSort)与归并排序(MergeSort)
- 归并排序(Merge Sort)
- 归并排序(merge sort)
- 十大基础实用算法补全——归并排序(MergeSort)
- MergeSort(归并排序)算法Java实现
- 归并排序(Merge-Sort)的C语言实现
- 面试之路(16)-归并排序详解(MergeSort)递归和非递归实现
- 归并排序(merge_sort)的C语言实现
- C语言-数据结构-归并排序(merge sort)-递归 迭代-源代码及分析
- 归并排序(Merge Sort)
- 归并排序(mergeSort)之递归实现
- 第七章 ALDS1_5_B:Merge Sort 归并排序
- 【内部排序】八:归并排序(Merge Sort)详解与代码
- 二路归并排序(Merge Sort)
- 归并排序(MERGE_SORT)
- merge sort(归并排序)
- 归并排序(Merge Sort)
- 数据结构和算法分析之排序篇--归并排序(Merge Sort)和常用排序算法时间复杂度比较(附赠记忆方法)
- 归并排序(MergeSort)