当我们要排序的文件太大以至于内存无法一次性装下或者在某些场景中有内存空间限制的时候,我们可以使用外部排序,将数据在外部存储器和内存之间来回交换,以达到排序的目的,这里我们先介绍外部排序算法,体会它面对大量数据时体现出的一种思想——分而治之,即分开去处理的过程。我们接触的大多排序直接是在内存进行的,所以与外部排序有着很强烈的区别,这里说一下它的基本思想是有必要的。
例如:给你2G的数据在硬盘上,但是你只有256M的内存可以使用,怎么使这么大的文件有序?
首先我们可以将2G的数据分成8份,分别加载到内存中进行排序,在内存中的排序方法可以用内部排序如快排等
这些已经排好序的数据块我们称之为顺串,经过以上处理整个数据成了8个顺串,现在我们可以将两个顺串通过内存合并成一个更大的新的顺串(长度为原来的两倍),经过四次合并处理就完成了
按照这个方法一直来回合并,一直合并到最终的一个顺串,此时排序完成
注意:合并操作几乎不需要内存,只需要读入两个元素,选择一个最大的(或最小的)输出,然后再读入,再选择
举个实际的例子,排升序为例说明:
设待排数据为:80,92,12,97,13,34,18,98,27,57,40,74,内存一次可以装三个数据
我们将数据分为四份
然后将每份读入内存,排序后读回硬盘
然后两两合并
输出哪个元素,就在那个元素所在的顺串(或者叫组)再次读入元素到内存
就这样,一直合并到两个顺串完为止(如果一个顺串先完,剩下另一个顺串的部分,那么就将剩下的顺串直接拷贝到硬盘上)
按照这个方法,把合并后的顺串继续合并,直到最终合并成一个总的顺串,排序结束
了解了外部排序的基本思想后,现在我们来看看今天的主题——归并排序
归并排序:
归并排序的基本思想是基于合并,将两个或两个以上的有序表合并成一个新的有序表。这里我们先以2—路归并为例,介绍归并排序算法
算法思想:
假设初始序列含有N个记录,首先将这N个记录看成N个有序的子序列,每个子序列的长度为1,然后两两归并,得到个长度为2的(N为奇数时最后一个序列的长度为1)有序子序列;再对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列;如此反复直到得到一个长度为N的有序子序列为止——这就是2—路归并排序
图形分析:
以序列{10, 4, 6, 3, 8, 2,5, 7} 为例来看看归并排序的划分和合并过程
代码实现:
void mergeArray(int a[],int first,int mid,int end,int temp[]) //合并两个有序区间的算法
{
int k = 0;
int i = first, m = mid;
int j = mid + 1, n = end;
while (i<=m && j<=n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i<=m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0;i < k;++i) //将有序串拷贝到原来的区间
a[first + i] = temp[i];
}
void mergesort(int a[],int first,int end,int temp[]) //递归划分
{
if (first < end)
{
int mid = (end + first) >> 1;
mergesort(a,first,mid,temp); //左边有序
mergesort(a,mid+1,end,temp); //右边有序
mergeArray(a,first,mid,end,temp); //合并两个有序数列
}
}
void MergeSort(int a[],int n)
{
int* p = new int[n];
assert(p);
mergesort(a,0,n-1,p);
delete[] p;
}
时间复杂度及空间复杂度:
算法名称\时间复杂度 | 最差时间复杂度 | 平均时间复杂度 | 最好时间复杂度 | 空间复杂度 |
归并排序 | O(NlogN) | O(NlogN) | O(NlogN) | O(N) |
稳定性:
该算法是稳定的
提示:这篇博客有些地方说的不是很详细,今晚不想写了,以后补充。。。