您的位置:首页 > 其它

归并排序(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),同时归并排序是稳定的,但是这种算法很消耗空间,一般来说在内部排序不会用这种方法,而是用快速排序;外部排序才会考虑到使用这种方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 排序