数据结构——外部排序的方法

外部排序的方法

外部排序的基本问题是如何在内存容量有限的情况下,对存储在外存中的大量数据进行排序。解决这个问题的关键在于将排序过程分解为多个阶段,每个阶段只需要处理能够装入内存的数据量,通过多次外存读写和内存排序的配合,最终完成整个文件的排序。外部排序最常用的方法是多路归并排序,它充分利用了归并排序的思想,将其扩展到外存环境。

1. 外部排序的两个阶段

外部排序的完整过程可以划分为两个明确的阶段,这两个阶段的配合是实现外部排序的基础。

第一个阶段是生成初始归并段阶段。在这个阶段,需要将外存文件分批读入内存,每次读入的数据量不超过内存容量,然后利用内部排序算法对这批数据进行排序,排序后将结果写回外存,形成一个有序的初始归并段。重复这个过程,直到将整个文件分解为若干个初始归并段。这些初始归并段虽然各自内部有序,但彼此之间并无顺序关系。假设待排序文件包含10000个记录,内存一次只能容纳1000个记录,那么需要读入10次,每次排序后写回外存,最终得到10个初始归并段。

第二个阶段是多路归并阶段。在这个阶段,需要对生成的初始归并段进行多次归并,逐步减少归并段的数量,增加每个归并段的长度,直到最终形成一个完整的有序文件。每次归并操作从若干个有序的归并段中各取出一部分记录到内存,通过比较选出最小值输出,形成新的更长的归并段。这个过程类似于内部排序中的归并排序,但需要在外存和内存之间频繁进行数据交换。

2. 二路归并的基本过程

为了更清楚地理解外部排序的工作原理,首先从最简单的二路归并情况入手进行分析。二路归并是指每次归并操作从两个有序归并段中选择记录进行合并。

假设有一个包含16个记录的文件需要排序,而内存一次只能容纳4个记录。外部排序的完整过程可以用下图表示。

第三趟二路归并
第二趟二路归并
第一趟生成初始归并段
初始文件
1, 3, 14, 15, 27, 29, 30, 38, 39, 46, 48, 49, 51, 52, 61, 63
14, 29, 38, 39, 46, 49, 51, 61
1, 3, 15, 27, 30, 48, 52, 63
39, 46, 49, 51
29, 38, 61, 14
1, 15, 30, 48
3, 27, 52, 63
51, 49, 39, 46, 38, 29, 14, 61, 15, 30, 1, 48, 52, 3, 63, 27

上图完整展示了外部排序的全过程。初始文件包含16个无序记录,第一趟处理时,每次读入4个记录到内存,进行内部排序后写回外存,生成4个初始归并段,每个归并段内部有序。第二趟进行二路归并,将相邻的两个归并段合并,前两个归并段合并成一个包含8个记录的有序段,后两个归并段也合并成一个包含8个记录的有序段,此时归并段数量减少为2个。第三趟继续二路归并,将剩余的两个归并段合并成一个包含全部16个记录的最终有序文件,排序完成。

在二路归并的每一趟中,需要为两个输入归并段各分配一个输入缓冲区,为输出归并段分配一个输出缓冲区。归并时,从两个输入缓冲区中各取出一个记录进行比较,将较小的记录输出到输出缓冲区。当某个输入缓冲区为空时,从对应的归并段继续读入一批记录;当输出缓冲区满时,将其内容写回外存。这个过程一直持续到两个输入归并段都处理完毕。

3. 多路归并的扩展

二路归并虽然思路清晰,但归并趟数较多,导致磁盘读写次数较多。为了减少归并趟数,可以采用多路归并的方法,即每次归并时同时处理kkk个归并段,而不仅仅是两个。

多路归并的基本思想是,在每趟归并时,同时从kkk个有序归并段中选择记录。具体做法是为kkk个输入归并段各分配一个输入缓冲区,再分配一个输出缓冲区。每次从kkk个输入缓冲区中选出关键字最小的记录输出。这个选择过程需要在kkk个记录中进行比较,可以通过建立一个大小为kkk的最小堆来优化,使得每次选择的时间复杂度为O(log⁡k)O(\log k)O(logk)

假设有8个初始归并段,采用4路归并,只需要两趟就能完成排序。第一趟将8个初始归并段两两一组,每组4个进行归并,生成2个更长的归并段。第二趟将这2个归并段进行最后的归并,得到最终的有序文件。相比二路归并需要3趟,4路归并减少了一趟归并操作。

第二趟2路归并
第一趟4路归并
8个初始归并段
最终有序文件
归并段A
归并段B
段1
段2
段3
段4
段5
段6
段7
段8

上图展示了4路归并的过程。8个初始归并段在第一趟中分为两组,每组4个段进行4路归并,生成2个更长的归并段。第二趟将这2个归并段进行归并,得到最终结果。整个过程只需要2趟归并,大大减少了磁盘访问次数。

多路归并的路数kkk受到内存大小的限制。假设内存可用空间为MMM,每个缓冲区大小为BBB,那么最多可以进行kkk路归并,其中k+1k+1k+1个缓冲区(kkk个输入缓冲区和1个输出缓冲区)的总大小不能超过MMM,即(k+1)×B≤M(k+1) \times B \leq M(k+1)×BM,可得k≤M/B−1k \leq M/B - 1kM/B1。增大路数kkk可以减少归并趟数,但也需要更多的内存空间用于缓冲区。

4. 归并趟数与磁盘访问次数

外部排序的效率主要取决于磁盘访问次数,而磁盘访问次数与归并趟数密切相关。假设初始归并段数量为rrr,采用kkk路归并,那么归并趟数sss可以用公式s=⌈log⁡kr⌉s = \lceil \log_k r \rceils=logkr计算。例如,若有16个初始归并段,采用4路归并,则s=⌈log⁡416⌉=2s = \lceil \log_4 16 \rceil = 2s=log416=2趟。

每趟归并都需要读入所有记录一次,输出所有记录一次,因此每趟需要2次完整的磁盘访问。加上生成初始归并段时的1次读和1次写,总的磁盘访问次数为2(s+1)2(s+1)2(s+1)次。继续上面的例子,2趟归并加上初始段生成,总共需要2×(2+1)=62 \times (2+1) = 62×(2+1)=6次磁盘访问。

减少归并趟数是提高外部排序效率的关键。可以通过两种方式实现:一是增加归并路数kkk,但受到内存容量的限制;二是减少初始归并段数量rrr,这需要增加内存容量或采用更高效的生成初始归并段的方法。在内存容量固定的情况下,需要在归并路数和初始归并段大小之间进行权衡,以达到最优的排序效率。这些优化方法将在后续内容中详细讨论。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据张老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值