实现思路
如何理解“归并”?简单来说,它就是将两个已经排好序的数组进行合并成一个有序数组的过程,就像下图需要维护三个索引,分别是两个子数组最小元素位置和要归并进去的数组位置。最后,依次遍历比较两个子数组中最小的元素,放到要归并的位置中。
代码实现
使用递归的思想,将数组一分为二,分别使用递归排序进行排序,之后,两部分就有序了,再将两部分进行归并起来,形成一个有序的数组,完成排序。
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
// 其中aux为完成merge过程所需要的辅助空间
template<typename T>
void __merge(T arr[], T aux[], int l, int mid, int r){
// 由于aux的大小和arr一样, 所以我们也不需要处理aux索引的偏移量
// 进一步节省了计算量:)
for (int i = l; i <= r; i++)
aux[i] = arr[i];
// 初始化,i指向左半部分的起始索引位置l;j指向右半部分起始索引位置mid+1
int i = l, j = mid + 1;
for (int k = l; k <= r; k++){
if (i > mid){ // 如果左半部分元素已经全部处理完毕
arr[k] = aux[j]; j++;
}
else if (j > r){ // 如果右半部分元素已经全部处理完毕
arr[k] = aux[i]; i++;
}
else if (aux[i] < aux[j]) { // 左半部分所指元素 < 右半部分所指元素
arr[k] = aux[i]; i++;
}
else{ // 左半部分所指元素 >= 右半部分所指元素
arr[k] = aux[j]; j++;
}
}
}
// 使用优化的归并排序算法, 对arr[l...r]的范围进行排序
// 其中aux为完成merge过程所需要的辅助空间
template<typename T>
void __mergeSort(T arr[], T aux[], int l, int r){
// 对于小规模数组, 使用插入排序
if (r - l <= 15){
insertionSort(arr, l, r);
return;
}
int mid = (l + r) / 2;
__mergeSort(arr, aux, l, mid);
__mergeSort(arr, aux, mid + 1, r);
// 对于arr[mid] <= arr[mid+1]的情况,不进行merge
// 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
if (arr[mid] > arr[mid + 1])
__merge(arr, aux, l, mid, r);
}
template<typename T>
void mergeSort(T arr[], int n){
// 在 mergeSort中, 我们一次性申请aux空间,
// 并将这个辅助空间以参数形式传递给完成归并排序的各个子函数
T *aux = new T[n];
__mergeSort(arr, aux, 0, n - 1);
delete[] aux; // 使用C++, new出来的空间不要忘记释放掉:)
}
性质与特性
- 如果要将两个有序数组进行合并,用这种方法更为合适;
- 归并排序是一个递归排序的过程;
- 归并排序的重点在于归并过程,要注意辅助空间的数组下标与原数组有 l <script type="math/tex" id="MathJax-Element-1">l</script>的偏移量。