上两节讲解的几种排序方法不是时间复杂度大了,就是不稳定,今天我们讲一下一种时间复杂度低又稳定的一种内部排序方法;
归并排序
基本思想:将两个或两个以上的有序序列合并成一个新的有序序列:
有序序列V[1] …V[m]和V[m+1] …V[n]
V[1] …V[n]
这种归并方法称为2路归并。
将3个有序序列归并为一个新的有序序列,称为3路归并。将多个有序序列归并为一个新的有序序列,称为多路归并。
利用归并的思想很容易实现排序,假设初始序列含有n个元素,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序序列;再两两归并,......,如此重复,直至得到一个长度为n的有序序列位置(如上描述,可以使用递归实现),这种排序方法称为2-路归并排序。
二路归并排序示例:
2-路归并排序中的核心操作是将一维数组中的前后相邻的两个有序序列归并为一个有序序列,其算法如下:
单趟归并实现代码如下:
// 归并函数
void Merge(int src[], int des[], int low, int mid, int high)
{
int i = low;
int j = mid + 1; // 存放序列中间元素的下标
int k = low;
// 当i和j都在两个序列内变化时, 根据关键码的大小将较小的数据
// 元素排放到新序列k所指位置中
while( (i <= mid) && (j <= high) )
{
if( src[i] < src[j] )
{
des[k++] = src[i++];
}
else
{
des[k++] = src[j++];
}
}
// 当i与j中有一个已经超出序列时,将另一个序列中的剩余部分照抄到新序列中
while( i <= mid )
{
des[k++] = src[i++];
}
while( j <= high )
{
des[k++] = src[j++];
}
}
递归归并排序代码:
// 归并排序
void MSort(int src[], int des[], int low, int high, int max)
{
// 排序结束
if( low == high )
{
des[low] = src[low];
}
else
{
int mid = (low + high) / 2; // 确定中间值
// 申请中间存储内存
int* space = (int*)malloc(sizeof(int) * max);
// 申请成功
if( space != NULL )
{
// 排序
MSort(src, space, low, mid, max);
MSort(src, space, mid+1, high, max);
// 归并
Merge(space, des, low, mid, high);
}
// 是否内存
free(space);
}
}
void MergeSort(int array[], int len) // O(n*logn)
{
MSort(array, array, 0, len-1, len);
}
测试代码
int main()
{
int array[] = {21, 25, 49, 25, 16, 8};
int len = sizeof(array) / sizeof(*array);
println(array, len);
MergeSort(array, len);
println(array, len);
return 0;
}
从代码中我们发现,归并排序不仅需要递归,还得需要和待排元素等数量的辅助空间,这样的话就会占用太多的内存。各种排序方法各有利弊,可以根据实际情况斟酌使用。当然,排序算法不仅仅只有上面介绍的这些,还有其他的这里暂时不再一一说明,以后再讨论。