大家好,欢迎来到IT知识分享网。
一、外部排序
1.1 基本思想
有时,待排序的文件很大,计算机内存不能容纳整个文件,这时候对文件就不能单纯的使用内部排序了,还得采取一些其他的策略。因此需要将待排序的记录存储到外存上,排序时再将数据一部分一部分地调用内存进行排序,在排序过程中需要多次进行内存和外存之间地交换,这种排序方法就成为外部排序。
文件通常是按块存储在磁盘上的,操作系统也是按块对磁盘上的信息进行读写的。因为磁盘读/写的机械动作所需的时间远远超过内存运算的时间,因此外部排序过程中的时间代价主要考虑访问磁盘的次数,即I/O次数。
外部排序常采用的排序方法是归并排序,这种归并方法由两个不同的阶段组成:
- 根据内存缓冲区大小,将外存中的文件分成若干个长度为 l l l的子文件,依次读入内存并利用内部排序方法对它们排序,并将排序后得到的有序子文件重新写回外存,称这些有序子文件为归并串或顺串;
- 对这些归并段进行逐趟归并,使归并段(有序子文件)逐渐由小到大,直至得到整个有序文件为止。
1.2 图解
(2)将 ( 36 , 8 , 26 ) (36,8,26) (36,8,26)和 ( 42 , 9 , 48 ) (42,9,48) (42,9,48)分别存入输入缓冲区 1 1 1和输入缓冲区 2 2 2
(3)将输入缓冲区 1 1 1和输入缓冲区 2 2 2的数据进行递增排序
(4)将输入缓冲区 1 1 1和输入缓冲区 2 2 2的数据通过输出缓冲区逐一写入外存,形成一个有序归并段
(5)对于 ( 1 , 37 , 25 ) (1,37,25) (1,37,25)和 ( 45 , 27 , 28 ) (45,27,28) (45,27,28)进行上述同样的操作后得到的结果:
(6) 对剩余 12 12 12块内存依次进行上述操作,总共需要进行 16 16 16次读操作和 16 16 16次写操作,得到初始归并段。
(7)第一次归并:读入归并段 1 1 1和归并段 2 2 2中的第一块磁盘(相对最小),进行排序:
(8)依次找出这两个输入缓冲区中最小元素,并将其移动到输出缓冲区中,当输出缓冲区满,则写入外存 ( 1 , 8 , 9 ) (1,8,9) (1,8,9)
(9)继续找出这剩余元素中的最小元素,直到某一个缓冲区中空,则读入其所属归并段的后一个内存块的数据,并继续进行上述操作。直到两个缓冲区都空,且归并段 1 1 1和归并段 2 2 2中的元素全部读入内存,此时归并段 1 1 1和归并段 2 2 2就得到了一个有序的递增序列。
输入归并段 1 1 1的第二块内存
排序完成,归并段 1 1 1和归并段 2 2 2递增有序
(10)对剩余的六个归并段进行上述操作,八个归并段→四个归并段
(11)第二次归并:继续采用此方法依次取出归并段 1 1 1和归并段 2 2 2(归并段 1 1 1为八个归并段时的归并段 1 1 1和归并段 2 2 2,归并段 2 2 2为八个归并段时的归并段 3 3 3和归并段 4 4 4)的各个块进行排序操作:四个归并段→两个归并段。
(12)第三次归并:继续排序归并段 1 、 2 1、2 1、2,形成最后的有序递增序列
1.3 外部排序的开销
总时间开销 = 内部排序所需时间 + 内部归并所需时间 + 外部读写所需时间
1.4 优化:多路平衡归并
若改用 4 4 4路归并排序,则只需 2 2 2趟归并,外部排序时的总读/写次数便减至 32 × 2 + 32 = 96 32×2+ 32= 96 32×2+32=96。因此,增大归并路数,可减少归并趟数,进而减少总的磁盘 I / O I/O I/O次数。
一般地,对 r r r个初始归并段,做 k k k路平衡归并,归并树可用严格 k k k叉树(即只有度为 k k k与度为 0 0 0的结点的 k k k叉树)来表示。第一趟可将 r r r个初始归并段归并为 ⌈ r / k ⌉ ⌈r/k⌉ ⌈r/k⌉个归并段,以后每趟归并将 m m m个归并段归并成 ⌈ m / k ⌉ ⌈m/k⌉ ⌈m/k⌉个归并段,直至最后形成一个大的归并段为止。 树的高度 − 1 = ⌈ l o g k r ⌉ = 归并趟数 S 树的高度-1=⌈log_kr⌉=归并趟数S 树的高度−1=⌈logkr⌉=归并趟数S。可见,只要增大归并路数 k k k,或减少初始归并段个数 r r r,都能减少归并趟数 S S S,进而减少读写磁盘的次数,达到提高外部排序速度的目的。
二、败者树
为了使内部归并不受 k k k的增大的影响,引入了败者树。败者树是属性选择排序的一种变体,可视为一棵完全二叉树。
- 将每个归并段的第一个元素作为叶子结点加入败者树中
- 从左至右、从上往下的更新分支节点的信息:判断其左右子树的大小,除了根节点(最上面那个结点)记录冠军来自哪个归并段外,其余各分支节点记录的是失败者来自哪个归并段。
- 取出最小的元素 1 1 1后,从其所属的归并段中取出下一个元素 6 6 6,依次与从叶子结点到根节点的各个结点所记录的败者信息进行对比。
引进败者树后,选出最小的关键字,仅需 l o g 2 k log_2k log2k次比较(向上取整)。
可见,使用败者树后,内部归并的比较次数与 k k k无关了。因此,只要内存空间允许,增大归并路数 k k k将有效地减少归并树的高度,从而减少 I O IO IO次数,提高外部排序的速度。
值得说明的是,归并路数k并不是越大越好。归并路数 k k k增大时,相应地需要增加输入缓冲区的个数。若可供使用的内存空间不变,势必要减少每个输入缓冲区的容量,使得内存、外存交换数据的次数增大。当 k k k值过大时,虽然归并趟数会减少,但读写外存的次数仍会增加。
三、置换选择排序
使用选择置换排序,可以让每个初始段的长度不再受限于内存工作区大小,设内存工作区最多容纳 w w w个数据:
- 将待排序文件 F I FI FI输入 w w w个数据到内存工作区 W A WA WA中
- 选择 W A WA WA中关键字最小的数据,输出到 F O FO FO中,并且用 M I N MIN MIN记录该最小关键字
- 若 F I FI FI不空,则从 F I FI FI中继续输入文件到 W A WA WA
- 从 W A WA WA中选出比 M I N MIN MIN更大的关键字的数据,输出并更新此最小关键字作为新 M I N MIN MIN
- 重复2~4直到 W A WA WA中的每个关键字都> M I N MIN MIN为止,由此得到一个新的归并段
- 重复2~5,直到 W A WA WA空,得到全部初始归并段
四、最佳归并树
- 性质和构造完全相同于哈弗曼树
- 与哈弗曼树的区别:
- k k k叉树,其中 k > 2 k > 2 k>2时:需要判断是否能满足构造完全 k k k叉树,若不满足,则需要添加长度为 0 0 0的“虚段”
- 若 ( 初始归并段数量 − 1 ) % ( k − 1 ) = 0 (初始归并段数量 – 1) \% (k – 1) = 0 (初始归并段数量−1)%(k−1)=0,则能构成完全 k k k叉树
- 若 ( 初始归并段数量 − 1 ) % ( k − 1 ) = u ≠ 0 (初始归并段数量 – 1) \% (k – 1)= u ≠ 0 (初始归并段数量−1)%(k−1)=u=0,则说明需要添加 ( k − 1 ) − u (k – 1)-u (k−1)−u个虚段才能构成完全二叉树
- k k k叉树,其中 k > 2 k > 2 k>2时:需要判断是否能满足构造完全 k k k叉树,若不满足,则需要添加长度为 0 0 0的“虚段”
参考文章
数据结构——外部排序
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/120984.html












