归并排序
基本思想:将数组A[0 … n-1]中的元素分成两个子数组A1[0 … n/2]和A2[n/2+1 … n-1]。分别对这两个子数组单独排序,然后将一排序的两个子数组归并成一个含有n个元素的有序数组。
归并排序包含不相邻元素的比较,但并不会直接交换。在合并两个已排序的数组时,如果遇到了相同的元素,只要保证前半部分数组优先于后半部分数组,相同元素的顺序就不会颠倒,所以归并排序属于稳定的非线性的排序算法。
归并排序算法虽然高效且稳定,但在处理过程中除了用于保存输入数据的数组外,还要临时占用一部分的内存空间。
归并排序是个原地的排序,空间可以为O(1)
1、时间复杂度
算法的递推关系:
若n=2k,则有
若2k<n<2k+1,
则时间复杂度为:T(n)=O(nlogn)
2、代码实现
const int maxn=100;
int temp[maxn];
void MergeSort(int *a,int low,int high)
{
if(low>=high)//代码段①
return;
int mid=(low+high)/2;
MergeSort(a,low,mid);
MergeSort(a,mid+1,high);
Merge(a,low,mid,high);
}
void Merge(int *a,int low,int mid,int high)
{
int i=low;
int j=mid+1;
int size=0;
for(;(i<=mid)&&(j<=high);size++)
{
if(a[i]<a[j])
temp[size]=a[i++];//代码段②
else
temp[size]=a[j++];
}
while(i<=mid)
temp[size++]=a[i++];
while(j<=high)
temp[size++]=a[j++];
for(i=0;i<size;i++)
{
a[low+i]=temp[i];
}
}
3、改进
代码段①:
if(low>=high)//代码段①
return;
可以进行改进,当长度<5或者<7,可以换成冒泡排序、选择排序,避免递归深度过大,效率能够提高。
代码段②:
for(;(i<=mid)&&(j<=high);size++)
{
if(a[i]<a[j])
temp[size]=a[i++];//代码段②
else
temp[size]=a[j++];
}
temp可以存储数据的索引,而非数据的值,避免数据的值过大的时候拷贝所耗费的时间与空间。
4、应用场景
外排序是指处理超过内存限度的数据的排序算法。通常将中间结果放在读写较慢的外存储器(通常是硬盘)上。
外排序通常采用“排序-归并”策略
- 排序阶段:读入能放在内存中的数据量,将其排序输出到临时文件,一次进行,将待排序数据组织为多个有序的临时文件。
- 归并阶段:将这些临时文件组合为大的有序文件。
【举例】
使用100M内存对于900MB的数据进行排序:
- 读入100M数据内存,用常规方式(如堆排序)排序。
- 将排序后的数据写入磁盘。
- 重复前两个步骤,得到9个100MB的块(临时文件)中。
- 将100M内存划分为10份,前9份中为输入缓冲区,第10份输出缓冲区。
(如前9份各8M,第10份18M;或10份大小同时为10M) - 执行九路归并算法,将结果输出到缓冲区
(若输出缓冲区满,将数据写至目标文件,清空缓冲区。若输入缓冲区空,读入相应文件的下一份数据)