算法描述
归并排序的思想是采用分治策略(二分),首先将序列(递归地)分解成若干子序列,然后合并,先使每个子序列有序,再合并两个子序列为一个有序序列,直到所有子序列合并成一个有序序列。
归并排序的特点
首先,归并排序是一种稳定的排序,所谓“稳定”,是指给定的待排序列中如果含有若干个相等的元素,在排序后,相等的元素之间的相对位置不会被改变。如:原始序列{5a,3a,4,2,6,1,3b,3c,7,5b},稳定的排序后输出应为{1,2,3a,3b,3c,4,5a,5b,6,7},注意排序后的多个3和多个5与待排前的相对位置是一致的。这对排序数据包含多个信息而要求按其中的某一个信息排序,要求其它信息尽量按输入的顺序排序时很重要!
其次,归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。
归并排序的过程
如上图(引用原图)所示,首先是分解的过程:可以理解为就是递归的拆分子序列的过程(其结构像一颗完全二叉树),递归深度为log2n。
其真正的排序核心在于“治”,即合并的过程:将两个已经排序的子序列(起初最小的子序列为只包含一个数的子序列,显然也属于排序好的子序列)合并为两个已经有序的子序列。其步骤如下图(引用原图)所示。
代码实现
归并步骤:
第一步:申请空间,其大小为两个已经排序序列之和,用来存放合并后的序列。
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置。
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置。
重复步骤三,直到某一指针超出序列尾。
第四部:将另一序列剩下的所有元素直接复制到合并序列尾部。
//C++ 版本 递归实现
#include<iostream>
using namespace std;
/*********************函数参数说明**********************
传入参数:数组的首地址 int* number
第一个已排序序列的起始索引 int start,第一个已排序序列的终止索引 int mid;
(第二个已排序序列的起始索引 mid + 1),第二个已排序序列的终止索引 int end;
********************************************************/
void Merge(int* numbers, int start, int mid, int end) {
int* temp = new int[end - start + 1]; //第一步,申请空间,大小为两个排序序列之和
int fistSectionIndex = start; //第二步,设定两个待排序列的起始位置的索引
int secondSectionIndex = mid + 1;
int tempIndex = 0; //所申请空间的索引
while (fistSectionIndex <= mid && secondSectionIndex <= end) { //直到两个序列中有一个到达终止位置
if (numbers[fistSectionIndex] <= numbers[secondSectionIndex])
temp[tempIndex++] = numbers[fistSectionIndex++];
else
temp[tempIndex++] = numbers[secondSectionIndex++];
}
while (fistSectionIndex <= mid)
temp[tempIndex++] = numbers[fistSectionIndex++];
while (secondSectionIndex <= end)
temp[tempIndex++] = numbers[secondSectionIndex++];
for (int j = 0; j < tempIndex; ++j) //将合并且排序好的元素,复制到原来的数组中,释放临时数组空间
numbers[start + j] = temp[j];
delete[] temp;
}
void MergeSort(int* numbers, int start, int end) {
if (numbers == NULL || start >= end)
return;
int mid = (start + end) / 2;
MergeSort(numbers, start, mid); //递归排序numbers[start,mid](首先从上往下递归分解到最底层元素个数为1的情况)
MergeSort(numbers, mid + 1, end); //递归排序numbers[mid + 1,end](首先从上往下递归分解到最底层元素个数为1的情况)
Merge(numbers, start, mid, end); //然后递归的从下往上合并排序
}
int main() {
int a[10] = { 5,6,1,8,3,4,9,7,2,3 };
cout << "归并排序前:";
for (int i = 0; i < 10; i++)
cout << a[i] << ' ';
cout << endl;
MergeSort(a, 0, 9);
cout << "归并排序后:";
for (int i = 0; i < 10; i++)
cout << a[i] << ' ';
cout << endl;
}
运行结果:
经典应用
求一段区间的逆序对数:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6),(7,5),(7,4),(6,4),(5,4)。