归并排序
归并排序(MergeSort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
分治法,也可以称为分治策略:是将一个大规模的问题(原问题)划分成n个规模较小而结构与原问题相似的子问题,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。
分治模式在每一层递归上都有三个步骤:分解、解决、合并。
简单来说归并排序类似于对两个有序序列的合并。
基本思想
将待排序元素序列分成两个长度相等的子序列,对每一个子序列排序,然后将他们合并成一个序列,合并两个子序列的过程称为二路归并。
归并排序的核心步骤: ●分组 ●归并
图解:
1.分组
在这样分组完毕之后,每个组内元素都只剩一个,那么一个元素一定是有序的。这时候对相邻的两个元素进行归并排序。两两排序完毕后再与其相邻组继续归并,直至结束。
2.归并
代码实现
●递归版本:
void MergeData(int arr[], int beg, int mid, int end, int* tmp)
{
int cur1 = beg;
int cur2 = mid;
int index = beg;
while (cur1 < mid && cur2 < end)
{
if (arr[cur1] < arr[cur2])
tmp[index++] = arr[cur1++];
else
tmp[index++] = arr[cur2++];
}
while (cur1 < mid)
{
tmp[index++] = arr[cur1++];
}
while (cur2 < end)
{
tmp[index++] = arr[cur2++];
}
}
void _MergeSort(int arr[], int beg, int end, int* tmp)// 分组
{
if (end - beg > 1)
{
int mid = beg + ((end - beg)>>1);
_MergeSort(arr, beg, mid, tmp);
_MergeSort(arr, mid, end, tmp);
MergeData(arr, beg, mid, end, tmp); //归并函数
memcpy(arr + beg, tmp + beg, (end - beg) * sizeof(int));// 将临时区域内有序数据拷贝至原数组
}
}
void MergeSort(int arr[], int size)
{
if (size <= 1)
return;
int* tmp = (int*)malloc(sizeof(int) * size);// 创建临时区域存放归并后的数组
if (NULL == tmp)
{
return;
}
_MergeSort(arr,0,size,tmp);
free(tmp);
tmp = NULL;
}
-----------------------------test.c----------------------------------
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void test()
{
int arr[] = { 9,5,2,7,4,3,8,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
MergeSort(arr, sz);
for (; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test();
system("pause");
return;
}
●非递归版本
void MergeData(int arr[], int beg, int mid, int end, int* tmp)
{
int cur1 = beg;
int cur2 = mid;
int index = beg;
while (cur1 < mid && cur2 < end)
{
if (arr[cur1] < arr[cur2])
tmp[index++] = arr[cur1++];
else
tmp[index++] = arr[cur2++];
}
while (cur1 < mid)
{
tmp[index++] = arr[cur1++];
}
while (cur2 < end)
{
tmp[index++] = arr[cur2++];
}
}
void MergeSortNor(int arr[], int size)
{
int gap = 1;
int i = 0;
int* temp = (int*)malloc(sizeof(int) * size);
if (NULL == temp)
{
return;
}
while (gap < size)
{
for (i = 0; i < size; i += 2 * gap)
{
int left = i;
int mid = i + gap;
int right = mid + gap;
//计算左右两半部分的区间
if (mid > size)
mid = size;
if (right > size)
right = size;
//检测左右半部分区间的合法性
MergeData(arr, left, mid, right, temp);
//归并
}
memcpy(arr, temp, size * sizeof(arr[0]));
gap <<= 1;
}
free(temp);
temp = NULL;
}
------------------------------------test.c-----------------------------------------
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void test()
{
int arr[] = { 9,5,2,7,4,3,8,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
MergeSortNor(arr, sz);
for (; i < sz; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
test();
system("pause");
return;
}
总结
归并排序算法的稳定性:稳定
归并排序算法的时间复杂度: O (N * log N)
适用场景:在用于排序一个整体无序但各个元素之间相对有序的序列时效率很高(外部排序)