归并排序概念:
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
归并操作:
归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。
如 设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
算法描述:
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
比较
归并排序是稳定的排序.即相等的元素的顺序不会改变.如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序.这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。
动态图演示:
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <vld.h>//动态内存头文件
static void Merge()
{
int *brr = (int*)malloc(len*sizeof(int));
assert(brr != NULL);
int i = 0;//brr下标
int low1 = 0;//第一归并段的起始下标,下标可取
int high1 = low1+gap-1;//第一个归并段的结束下标,下标可取
int low2 = high1+1;//第二个归并段的起始下标,下标可取
int high2 = low+gap-1<len-1?low+gap-1:len-1;//第二个归并段的结束下标,下标可取
//有两个归并段
while(low2 < len)
{
//两个归并段都有数据
while(low1 <= high1 && low2 <= high2)
{
if(arr[low1] <= arr[low2])
{
brr[i++] = arr[low1++];
}
else
{
brr[i++] = arr[low2++];
}
}
//一个归并段没有数据,另外一个还有数据
while(low1 <= high1)
{
brr[i++] = arr[low1++];
}
while(low2 <= high2)
{
brr[i++] = arr[low2++];
}
low1 = high2 +1;
high1 = low1 + gap-1;
low2 = high1 +1;
high2 = low2 +gap-1 < len-1?low2+gap-1:len-1;
}
//不足两个归并段
while(low1 < len)
{
brr[i++] = arr[low1++];
}
for(i =0;i<len;i++)
{
arr[i] = brr[i];
}
free(brr);
}
void MergeSort(int *arr ,int len)
{
for(int i=1; i<len ;i* = 2)
{
Merge(arr,len,i);
}
}
void Show(int *arr ,int len)
{
for(int i =0 ;i<len ;i++)
{
printf("%d",ar[i]);
}
printf("\n");
}
int main()
{
int arr[] = {2,34,21,54,32,89,76,78.90};
Mergesort(arr,sizeof(arr)/sizeof(arr[0]));
return 0;
}
非递归: