数据结构--Chapter8(外排序)

来源:互联网 发布:知柏地黄丸一次吃多少 编辑:程序博客网 时间:2024/06/07 09:32

8 外排序

    上一章节中介绍的各种排序方法,其待排序的记录及其相关信息都是存储在内存中,无需借助外村就能完成整个的排序过程,这些排序叫做内部排序。但当待排序的记录其数据量较大时,则无法在内存中完成整体排序,为此需要将待排序的记录以文件的形式存储在外存储器中,排序时每次只能将文件中的部分记录数据装入内存进行处理,这样,要达到对文件整体排序的目的,则需要再内存和外存之间进行多次数据交换。像这种需要借助外存储器才能完成整个排序过程的排序就叫外排序。

8.1 外存的存取特性

1. 磁盘

    磁盘是一种随机存取(直接存取)的存储设备,可以直接存取设备上的数据。磁盘由盘片、盘片主轴、磁头和磁头控制器组成,其中,盘片是用于存储数据的,盘片被划分成多个同心圆,称为磁道,磁道由外向内从0开始顺序编号,所有的数据信息被记录在磁道上。磁盘一般由多个盘片组成,每一个盘片包含两个面,其中,最上面和最下面的外侧一般不存储信息。例如,一个磁盘由6个盘片组成,则有10个面可保存信息。

    所有的盘面的同一磁道构成一个圆柱,称为柱面,位于同一柱面上的磁道由上往下从0开始编号。每个磁道还可以划分成若干个部分,称为扇区,每个扇区的大小是512字节。扇区所在的磁道地址称为扇区号。

    例如,某磁盘有10个有效记录面,记录面上有效记录区域的内径为20cm,外径为30cm,道密度为10道/mm,每个磁道有16个扇区,每个扇区记录512字节,则该磁盘容量是:  记录面数x磁道数x磁道扇区数x扇区的字节数=10x(30-20)/2x10x10x16x512=40960000字节。

    要存取某一数据信息,首先需要找到数据所在的柱面,移动磁头到所在的柱面即磁道,然后移动磁头到具体的数据存放位置,因此,要存取磁盘上的数据信息所需要的时间由3部分组成:寻道时间、等待时间和传输时间。其中,寻道时间Tseek就是读写磁头寻找数据所在磁道并定位的时间,等待时间Tw就是将磁头移动到数据所在的起始位置,传输时间Trw就是读或写数据所需要的时间。即T=Tseek+Tw+Trw

2. 磁带

    磁带作为主要的存储介质,一般磁带长3600英尺、宽0.5英尺。磁带可分为7道带和9道带,7道带的磁带,每一排可以存储8位即一个字节,其中7位是数据位,1位是奇偶校验位。通常情况下,磁带的存储密度是每英寸800位或1600位,磁带的移动磁头速度是每秒200英寸。

    磁带是一种启停设备,当磁头正在读写磁带上的记录时,如果要停止读写,必须经过一个减速过程,然后才能停止。同样,从磁头静止到开始读取数据时,磁头是经过一个加速旋转的过程,然后才能均匀的旋转达到稳定状态。因此,在磁带的每两个相邻记录之间,需要一个空白区域,这个空白区域称为间隙。间隙的大小通常是0.25英寸-0.75英寸。磁带的存储结构如下图:


8.2 外排序概述

    文件存储在外存上,因此,外部排序方法与各种外部设备的特性有关。外存设备一般分为两大类,一类是顺序存储设备,如磁带;另一类是直接存储设备,如磁盘。这儿讨论的是磁盘排序。

    外部排序最基本的方法是归并排序法,先按可用内存大小,将外存上含n个记录的文件分成若干个长度为l(l<n)的子文件或段,依次读入内存,再用一种有效的内部排序方法对文件的各个段进行排序,并将排序后得到的有序子文件重新写入外存,通常将排序后的子文件称为顺串(或归并段);第二阶段是进行多路归并,即采用多路归并方法对顺串进行逐趟归并,是顺串长度逐渐由小变大,直至得到整个有序文件为止。最初形成的顺串文件长度取决于内存所能提供的排序区大小和最初排序的策略,而归并路数取决于能提供的外存设备数。总结如下:

1)生成若干初始顺串(归并段)。将外存上含n个记录的文件分成若干个长度为l(l<n)的子文件或段,依次读入内存

2)多路归并。对初始顺串逐趟合并,直至在外存上得到整个有序文件。

    外排序的时间消耗由生成初始顺串的时间、外存信息读/写时间、内部归并排序时间,但影响最大的是外存信息读写时间。对同一文件而言,进行外排序时所需要的外存信息读/写次数和内部归并的趟数是成正比的。

    对m个初始顺串采用k路归并时,归并的趟数s为:s=[logkm](向上取整)。所以,增加k或减少m都能减少s。所以:

1)减少初始顺串的个数m,由于记录的个数是一定的,即增加初始顺串的长度。

2)增加归并路数k,即多路归并。

8.2.1 多路平衡归并

     对m个归并段采用k路归并时,需要进行s=[logkm](向上取整趟的归并,显然增加k,可减少归并的趟数,从而达到减少对外存的读/写次数,但是增加k会使得归并效率变低。

    如果利用“败者树”进行k路平衡归并,则可在k个记录中选出关键字最小/最大的记录时仅需进行[log2k](向上取整)次比较,从而使总比较次数变为[log2m](n-1),与k无关。

     败者树是一棵完全二叉树,其中每个叶子结点存放各归并段在归并过程中当前参加比较的记录;每个非叶结点记忆其两个子女结点中记录关键字中较大者的结点(即败者)。由于它是完全二叉树,因此,可以采用一维数组作为存储结构,假设一维数组为LS,树中元素有k个叶子结点,k-1个比较结点、1个冠军结点,所以以为数组共有2k个元素。其中,ls[0]为冠军结点,ls[1]--ls[k-1]为比较结点,ls[k]--ls[2k-1]为叶子结点,ls[k]--ls[2k-1]为叶子结点,同时用另外一个指针索引b[0]--b[k-1]指向叶子结点,b为一个附加的辅助空间,不属于败者树,初始化时存放一个含最小关键字MINKEY的叶子结点。

    利用败者树对k个初始归并段进行k路平衡归并排序的具体方法描述如下:

1)首先将k个归并段中第一条记录的关键字一次存入b[0]...b[k--1]中作为叶子结点创建初始败者树。

2)根据ls[0]的值确定最小关键字所在的归并段序号q,将该归并段的第一条记录输出到有序归并段,然后再将此归并段中的下一条记录的关键字存入上一条记录本身所在的叶子结点b[q]中。

3)重构败者树。将新的b[q]这个叶子结点与父结点进行比较,大的存放在父结点,小的与上一级父结点再进行比较,如此逐层向上调整,直到根结点,最后将选出的新的最小关键字下标同样存在ls[0]中。

4)重复步骤2)和3),直至所有记录都被写入到有序归并段中为止。

8.2.2 置换—选择排序

    生成初始归并段可采用内部排序法和置换—选择排序法。用内部排序法是按照可用内存空间大小将待排序的部分记录读入内存,然后用一种内部排序法进行排序,排序后再写入外存从而生成一个初始归并段。这种方法生成的所有初始归并段的长度只与可用内存空间大小有关,除了最后一个初始归并段它的长度可能小于l之外,其他的长度都与l相等,个数为[n/l]向上取整,其中,n为待排序的文件记录个数,l是可用内存空间大小。

    用置换—选择排序法则可以生成长度不等的初始归并段,且长度比l大,当输入文件中的记录是按其关键字随机排序时,所生成的初始归并段的平均长度为可用内存空间大小的两倍。因此,用置换—选择排序法可增加初始归并段的长度,从而减少生成的初始归并段的数目,进而减少多路归并的趟数。特别地,当输入文件记录以关键字顺序排序时,可得到一个长度与文件长度相等的归并段;当输入文件记录以关键字逆序时,得到的初始归并段的最大长度为可用内存空间的大小,最小长度为1。

     假设输入文件FI是待排序文件,输出文件FO存放初始顺串,WA是可以容纳m个记录的内存工作区,则置换—排序的基本过程为:

1)从FI输入m个记录到内存工作区WA。

2)从WA中选出关键字最小的记录MIN,其关键字为MINKEY。

3)将MIN输出到FO中。

4)若FI不空,则从FI中读入下一个记录,送到WA中。

5)从WA中所有的关键字大于MINKEY的记录中,重新选出关键字最小的记录MIN。

6)重复3)至5),直到在WA中选不出新的MIN记录为止,此时已得到一个初始顺串,所以输出一个结束标志到FO中。

7)重复2)至6),直到WA为空。此时,FO中将顺次存放着所有的初始顺串。

    下面演示置换—选择排序法的过程


    使用置换—选择排序法得到的顺串特点:

1)每个顺串内按关键字有序。

2)前一个顺串最后一条记录的关键字一定大于后一个顺串的第一个关键字。

    在工作区WA中选出MIN记录的过程要用到败者树来实现。


    这儿是用Java实现的外排序,多路归并时使用的是败者树,生成初始归并段时使用的是内部排序,不是置换—选择排序。参考代码

0 0
原创粉丝点击