归并排序简单地将原始数组划分为两个子数组,然后对每个子数组递归的排序,最后再将有序子数组合并,归并排序的主要步骤为:
(1)将数组划分为两个子数组。 (2)分别对两个子数组递归进行合并排序。 (3)将这两个已经排好序的子数组合并为为一个有序的数组。
归并排序是一种基于分治法的排序。归并排序的总的时间代价为o(nlogn),空间复杂度为o(n)。
原始优化的归并排序:
#include<iostream>
#include<time.h>
using namespace std;
//两个有序的子数组,合并为一个有序的数组
void merge(int arr[], int arrTmp[], int start, int end, int middle)
{
//将数组暂时存入临时数组中
for(int i = start; i <= end; ++i)
{
arrTmp[i] = arr[i];
}
//左边数组的起始位置
int index1 = start;
//右边数组的起始位置
int index2 = middle + 1;
//原始数组的起始位置
int pos = start;
while(index1 <= middle && index2 <= end)
{
if(arrTmp[index1] < arrTmp[index2])
{
arr[pos++] = arrTmp[index1++];
}
else
{
arr[pos++] = arrTmp[index2++];
}
}
while(index1 <= middle)
{
arr[pos++] = arrTmp[index1++];
}
while(index2 <= middle)
{
arr[pos++] = arrTmp[index2++];
}
}
void mergeSort(int arr[], int arrTmp[], int start, int end)
{
if(start < end)
{
int mid = (start + end) / 2;
mergeSort(arr,arrTmp,start,mid);//对数组前半部分进行递归
mergeSort(arr,arrTmp,mid + 1, end);//对数组后半部分进行递归
merge(arr,arrTmp,start,end,mid);//进行归并
}
}
int main(void)
{
cout<<"请输入数组的大小";
int num;
cin>>num;
int *arr = new int[num];
int *arrTmp = new int[num]();
for(int i = 0; i < num; ++i)
{
arr[i] = rand();
}
clock_t start,end;
start = clock();
mergeSort(arr,arrTmp,0,num - 1);
end = clock();
cout<<"排序时间:"<<float(end - start)/CLOCKS_PER_SEC*1000<<endl;
system("pause");
return 0;
}
实验结果:
请输入数组的大小:100000
排序时间:13
请输入数组的大小:1000000
排序时间:130
请输入数组的大小:10000000
排序时间:1550
请输入数组的大小:100000000
排序时间:16479
相同的数据集,归并排序比上一节中的快排要慢一点。
优化的归并排序:把数组暂时复制到临时数组时,将第二个数组中元素的顺序颠倒一下。这样,两个子数组从两端开始处理,向中间推进,使得这两个数组的两端互相成为另一个数组的监视哨。另一方面的优化是,当待排子数组中元素小于某个给定的值(50)时,则对子数组进行插入排序。
优化的归并排序:
#include<iostream>
#include<time.h>
using namespace std;
//优化的两个子数组归并,右数组逆置,都从两端向中间扫描,归并到新数组中。
void merge(int arr[], int arrTmp[], int start, int end, int middle)
{
//复制左边的数组
for(int i = start; i <= middle; ++i)
{
arrTmp[i] = arr[i];
}
//逆置复制右边的数组
int i = middle + 1;
int j = end;
while(i <= end && j > middle)
{
arrTmp[i++] = arr[j--];
}
//数组归并到原数组中。
int index1 = start;
int index2 = end;
int pos = start;
while(index1 <= index2)
{
if(arrTmp[index1] < arrTmp[index2])
{
arr[pos++] = arrTmp[index1++];
}
else
{
arr[pos++] = arrTmp[index2--];
}
}
}
void insert_sort(int arr[], int n)
{
for(int i = 1; i < n; ++i)
{
for(int j = i; j > 0; j--)
{
if(arr[j] < arr[j - 1])
{
swap(arr[j], arr[j - 1]);
}
else
{
break;
}
}
}
}
void mergeSort(int arr[], int arrTmp[], int start, int end)
{
if(end - start + 1 > 50)
{
int mid = (start + end) / 2;
mergeSort(arr,arrTmp,start,mid);
mergeSort(arr,arrTmp,mid + 1, end);
merge(arr,arrTmp,start,end,mid);
}
else//当待排序的子数组中元素的个数小于50时,使用插入排序
{
insert_sort(arr + start, end - start + 1);
}
}
int main(void)
{
cout<<"请输入数组的大小:";
int num;
cin>>num;
int *arr = new int[num];
int *arrTmp = new int[num]();
for(int i = 0; i < num; ++i)
{
arr[i] = rand();
}
clock_t start,end;
start = clock();
mergeSort(arr,arrTmp,0,num - 1);
end = clock();
cout<<"排序时间:"<<float(end - start)/CLOCKS_PER_SEC*1000<<endl;
system("pause");
return 0;
}
实验结果:
请输入数组的大小:100000
排序时间:19
请输入数组的大小:1000000
排序时间:121
请输入数组的大小:10000000
排序时间:1316
请输入数组的大小:100000000
排序时间:14791
优化以后的归并排序算法比原算法要快一些,但是,这种优化并没有从数量级上改善原来的算法时间代价仍然为o(nlogn),空间代价为o(n)。