算法思想
归并排序使用了分治的套路,先将待排序元素划分为个数均等的两部分,然后在对已经划分的两个部分在进行均等
划分,如此递归的划分下去,直到区间无法在划分时停止,然后合并这些子区间即可;合并时每次从两个子区间内选
出最大(降序排序)或者最小(升序排序)的元素放入辅助空间内,直到某个子区间为空,然后在将另一个子区间剩余
的元素全部追加到辅助空间内,然后在将辅助空间的元素覆盖到原来两个子区间即可
归并排序的主要代码是合并,例如:{1,13,24,26},{2,15,27,38}这两个已经有序的子区间进行合并,其过程如下:

从上图可以看出:此时i指向的元素1比j指向的元素2小,所以将1放入辅助空间:

从上图可以看出:此时i指向的元素13比j指向的元2大,所以将2放入辅助空间:

从上图可以看出:此时i指向的元素13比j指向的元15小,所以将13放入辅助空间:

从上图可以看出:此时i指向的元素24比j指向的元15大,所以将15放入辅助空间:

从上图可以看出:此时i指向的元素24比j指向的元27小,所以将24放入辅助空间:

从上图可以看出:此时i指向的元素26比j指向的元27小,所以将26放入辅助空间:

此时这一对区间的左侧区间已经复制到辅助空间中,在将右侧区间的剩余元素追加到辅助空间尾部:

至此来两个区间合并暂未完成!!!,还得把辅助空间中的元素覆盖到这对区间中,才算完成
例:对38,27,43,3,9,82,10进行升序排序
先对要排序的数组进行划分:

此时区间划分完成,开始从底向上进行合并:
因为在划分时,{38}没有对应的子区间,所以合并时不发生改变:

合并{27},{43}后,如下图:

合并{38},{27,43}后,如下图:

至此,第一次划分时的左侧区间已经有序,则对右侧进行合并:
合并{3},{9}后,如下图:

合并{82},{10}后,如下图:

合并{3,9},{82,10}后,如下图:

此时在进行最后一次合并则排序完成:
代码实现
//************************************************************************
// 函数名称: Merge
// 函数功能: 合并两个有序的分组
// 返回值: void
// 参数: int * pUnSortAry:存放两个有序的分组
// 参数: int * pTempAry:用于合并两个分组的临时数组,与pUnSortAry指向的数组同大小
// 参数: int nLeftPos:第一个有序数组的起始下标
// 参数: int nMiddlePos:第一个有序数组的终止下标,也是第二个有序数组的起始下标
// 参数: int nRightPos:第二个有序数组的终止下标
// 注意:
//************************************************************************
void Merge(int * pUnSortAry, int * pTempAry, int nLeftPos, int nMiddlePos, int nRightPos)
{
int nLeftIndex = nLeftPos;
int nRightIndex = nMiddlePos;
int nInsertIndex = 0;
/*对从两个分组中选取的元素进行比较,将较大元素放到临时数组中*/
while (nLeftIndex < nMiddlePos && nRightIndex < nRightPos)
{
if (pUnSortAry[nLeftIndex] < pUnSortAry[nRightIndex])
{
pTempAry[nInsertIndex] = pUnSortAry[nLeftIndex];
nLeftIndex++;
}
else
{
pTempAry[nInsertIndex] = pUnSortAry[nRightIndex];
nRightIndex++;
}
nInsertIndex++;
}
/*可能会有一个分组有剩余元素,则直接将这个分组中的剩余元素追加到临时数组的尾部*/
while (nLeftIndex < nMiddlePos)
{
pTempAry[nInsertIndex] = pUnSortAry[nLeftIndex];
nInsertIndex++;
nLeftIndex++;
}
while (nRightIndex < nRightPos)
{
pTempAry[nInsertIndex] = pUnSortAry[nRightIndex];
nInsertIndex++;
nRightIndex++;
}
/*将临时数组中的元素复制到原待排序数组中*/
for (nInsertIndex = nLeftPos; nInsertIndex < nRightPos; nInsertIndex++)
{
pUnSortAry[nInsertIndex] = pTempAry[nInsertIndex-nLeftPos];
}
}
//************************************************************************
// 函数名称: MergeSortInternal
// 函数功能: 对一个待排序数组进行划分,并排序
// 返回值: void
// 参数: int * pUnSortAry:待排序数组
// 参数: int * pTempAry:临时数组,与待排序数组等大小
// 参数: int nStartIndex:待排序数组起始下标
// 参数: int nEndIndex:待排序数组结束下标(指向数组最后一个元素的下一个位置)
// 注意:
//************************************************************************
void MergeSortInternal(int * pUnSortAry, int * pTempAry, int nStartIndex, int nEndIndex)
{
if (nStartIndex+1 < nEndIndex)
{
int nMiddleIndex = (nStartIndex + nEndIndex) / 2;
MergeSortInternal(pUnSortAry, pTempAry, nStartIndex, nMiddleIndex);
MergeSortInternal(pUnSortAry, pTempAry, nMiddleIndex, nEndIndex);
Merge(pUnSortAry, pTempAry, nStartIndex, nMiddleIndex, nEndIndex);
}
}
//************************************************************************
// 函数名称: MergeSort
// 函数功能: 实现归并排序
// 返回值: bool:成功返回true,否则false
// 参数: int * pUnSortAry:指向待排序数组
// 参数: int nSize:数组大小
// 注意:
//************************************************************************
bool MergeSort(int * pUnSortAry, int nSize)
{
int * pTempAry = (int *)malloc(sizeof(int)*nSize);
if (pTempAry == nullptr)
{
return false;
}
MergeSortInternal(pUnSortAry, pTempAry,0, nSize);
free(pTempAry);
return true;
}
测试代码如下:
void PrintData(int*pAry, int nSize)
{
for (int jIndex = 0; jIndex < nSize; jIndex++)
{
printf("%d ", pAry[jIndex]);
}
printf("\r\n");
}
int main(int argc, char*argv[])
{
srand(time(NULL));
int nArry[30] = { 0 };
for (int jIndex = 0; jIndex < 10; jIndex++)
{
for (int iIndex = 0; iIndex < sizeof(nArry) / sizeof(nArry[0]); iIndex++)
{
nArry[iIndex] = rand() % 150;
}
printf("排序前:");
PrintData(nArry, sizeof(nArry) / sizeof(nArry[0]));
MergeSort(nArry, sizeof(nArry) / sizeof(nArry[0]));
printf("排序后:");
PrintData(nArry, sizeof(nArry) / sizeof(nArry[0]));
printf("\r\n");
}
return 0;
}
运行结果:
时空复杂度分析
归并排序要用到辅助空间,其大小等同于数组的大小,而归并排序是递归实现的,所以其运行时栈空间消耗为lgn
所以总的空间复杂度为S(n) = n+lgn = O(n);
归并排序的合并操作的时间复杂度为Θ(n),总的时间复杂度为:T(n)=2T(n/2)+Θ(n)=nlgn
稳定性
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换)
然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时
1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程
中,稳定性没有受到破坏,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在
结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。