1.算法描述
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治思想(Divide and Conquer)的一个非常典型的应用。
-
分治思想:
-
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法步骤:
[1] 把长度为n的输入序列分成两个长度为n/2的子序列;
[2] 对这两个子序列分别采用归并排序;
[3] 将两个排序好的子序列合并成一个最终的排序序列。 -
动态图演示:
2.算法实现及优化
- 1.纯merge算法&改进过的merge算法
//Author:cheng.jiang
#ifndef ADVANCE_MERGESORT_H
#define ADVANCE_MERGESORT_H
#include <iostream>
using namespace std;
//自底部向下实现
// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
T aux[r-l+1];//辅助空间
for( int i= l; i <= r ; i++){
aux[i-l]=arr[i];//aux从0开始索引
}
int i=l, j=mid+1;//设置辅助空间开头索引号
for(int k=l; k<=r; k++){//从小到大排序
//判断i,j索引的合法性
if( i > mid){
arr[k]=aux[j-l];
j++;
}
else if( j > r){
arr[k]=aux[i-l];
i++;
}
//通过辅助空间的i,j索引赋值给arr[k]
else if( aux[i-l]<aux[j-l]){
arr[k]=aux[i-l];
i++;
}
else{
arr[k]=aux[j-l];
j++;
}
}
}
// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r){
if( l >= r )
return;
int mid = (l+r)/2;
__mergeSort(arr, l, mid);
//从左边开始二分到底,
//直到左边等于右边1=1停止调用:
//最后一层mergeSort(arr,1,1);mergeSort(arr,2,2);merge(arr,1,1,2)
__mergeSort(arr, mid+1, r);
__merge(arr, l, mid, r);
}
//定义mergeSort()函数
template<typename T>
void mergeSort(T arr[], int n){
__mergeSort( arr , 0 , n-1 );
}
// 使用优化的归并排序算法, 对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort2(T arr[], int l, int r){
// 优化2: 对于小规模数组, 使用插入排序
if( r - l <= 15 ){
insertionSort(arr, l, r);
return;
}
int mid = (l+r)/2;
__mergeSort2(arr, l, mid);
__mergeSort2(arr, mid+1, r);
// 优化1: 对于arr[mid] <= arr[mid+1]的情况,不进行merge
// 对于近乎有序的数组非常有效,但是对于一般情况,有一定的性能损失
if( arr[mid] > arr[mid+1] )
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort2(T arr[], int n){
__mergeSort2( arr , 0 , n-1 );
}
#endif //ADVANCE_MERGESORT_H
- 2.测试时耗
int main(){
int n = 50000;
// 测试1 测试随机数组
cout<<"Test for random array, size = "<<n<<", random range [0, "<<n<<"]"<<endl;
int* arr1 = SortTestHelper::generateRandomArray(n,0,n);
int* arr2 = SortTestHelper::copyIntArray(arr1, n);
int* arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);
SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
cout<<endl;
// 测试2 测试近乎有序的数组
int swapTimes = 10;
assert( swapTimes >= 0 );
cout<<"Test for nearly ordered array, size = "<<n<<", swap time = "<<swapTimes<<endl;
arr1 = SortTestHelper::generateNearlyOrderedArray(n,swapTimes);
arr2 = SortTestHelper::copyIntArray(arr1, n);
arr3 = SortTestHelper::copyIntArray(arr1, n);
SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);
SortTestHelper::testSort("Merge Sort 2", mergeSort2, arr3, n);
delete[] arr1;
delete[] arr2;
delete[] arr3;
return 0;
}
- 实验结果:
3.算法分析
- 归并排序是一种稳定的排序方法。数组的初始顺序会影响到排序过程中的比较次数,但是总的而言,对复杂度没有影响。平均情况 or 最坏情况下 它的复杂度都是 O ( n l o g n ) O_{(nlogn)} O(nlogn)。
- 代价是需要额外的内存空间,故空间复杂度为 O ( n ) O_{(n)} O(n)
- 归并排序的时耗主要集中在最后几步,可以通过改成插入排序来提高时间性能