【外排序】外排序算法(磁盘排序、磁带排序) 外存设备结构分析 败者树多路归并 最佳归并树白话讲解

数据结构 专栏收录该内容
17 篇文章 0 订阅

外排序概述

(本章设计内容后续会具体讲解)

外排序是指数据存放在外存中,数据排序时涉及内、外存数据交换的排序方法。 
在这里插入图片描述

存储在外存上的数据以文件为基本单位,由文件系统进行读写操作,读写操作的基本单位为物理块

外排序的基本方法是归并排序法

一、生成若干初始归并段(顺串):这一过程也称为文件预处理。一种常规的方法如下:
在这里插入图片描述

  1. 把含有n个记录的文件,按内存大小w分成若干长度为w的子文件(归并段);
  2. 分别将各子文件(归并段)调入内存,采用有效的内排序方法排序后送回外存。

    产生m=[n/w]个初始归并段。

此时产生的若干子文件称为顺串

二、多路归并:

(所谓多路归并,具体使用几路归并是看计算机内存大小的,如计算机内存为2则只能使用2路归并)

  对这些初始归并段进行多遍归并,使得有序的归并段逐渐扩大,最后在外存上形成整个文件的单一归并段,也就完成了这个文件的外排序。

例子

在这里插入图片描述

  1. 将一个大文件分成M个小文件,每个小文件是有序的
    在这里插入图片描述
  2. 然后我们从M个子文件中各取一个数字进入内存处理(将该数字打上队列编号标记方便后续处理)
    在这里插入图片描述
  3. 比较中转站中的数据,当从中转站出来的最小数字(升序排列)就是我们最后要排序的数字之一,将其写入到归并结果文件
    在这里插入图片描述

  因为该数字打上了队列编号,所以方便我们通知对应的编号队列继续出数字进入中转站队列(保证从中转站中取出的数据是所有文件中最小的),可以看出中转站一直保存了M个记录,当中转站中的所有数字都出队完毕,则本次归并结束。

因为每个小文件都是有序的顺串,所以当内存取出所有小文件第一个数据处理后,将从输出的那个数据所属小文件中再次拿出一个数据,保证了内存中的数据都是各个小文件中最小的那一个

多次使用归并算法,直到所有小文件都被处理完(以二路归并为例):
在这里插入图片描述

总结

  1. u个记录需要进行u-1次操作(对各个顺串中数据进行比较)(不考虑∞ (文件中数据读取完再取的话设为∞))

  2. k路归并每次操作需要k-1次关键字比较

  3. 总的关键字比较次数为:(u-1)(k-1)

  4. 影响归并过程性能因素:

    • 记录读写次数。

    • 内存中归并时需要关键字比较次数。

    • 外排序方法与各种外存设备的特征有关。

      影响内排序的数据位置交换,在外排序中变成了记录读写次数,所以此时影响外排序方法性能的主要原因为外存存取数据所花费的时间,即外排序方法与各种外存设备的特征有关

      外存设备大体上可以分为两类
      1.顺序存取设备(磁带)
      2.直接存取设备(磁盘)

存储设备(可忽略)

文件存储在外存上,因此外排序方法与各种外存设备的特征有关

外存设备大体上可以分为两类

  1. 顺序存取设备(磁带)
  2. 直接存取设备(磁盘)

磁带

磁带是一种典型顺序存取设备,通过读写头读写数据,对于检索和修改操作十分不便,主要用于处理很少需要修改的并且进行顺序存区的信息,特别用作备份数据的设备

磁带按用途可大致分成录音带、录像带、计算机带和仪表磁带四种。

磁带结构

在这里插入图片描述

  • 录音磁头实际上是个蹄形电磁铁,两极相距很近,中间只留个狭缝。整个磁头封在金属壳内。录音磁带的带基上涂着一层磁粉,实际上就是许多铁磁性小颗粒。
  • 磁带紧贴着录音磁头走过,音频电流使得录音头缝隙处磁场的强弱、方向不断变化,磁带上的磁粉也就被磁化成一个个磁极方向和磁性强弱各不相同的“小磁铁”,声音信号就这样记录在磁带上了。
  • 放音头的结构和录音头相似。当磁带从放音头的狭缝前走过时,磁带上“小磁铁”产生的磁场穿过放音头的线圈。由于“小磁铁”的极性和磁性强弱各不相同,它在线圈内产生的磁通量也在不断变化,于是在线圈中产生感应电流,放大后就可以在扬声器中发出声音。

磁盘

  磁盘是一种直接存取的外存设备,磁盘不仅能够进行顺序存取,而且能直接存取任何记录,他的存取速度比磁盘快得多

  磁盘分为硬盘和软盘两种,硬盘的容量比软盘大得多,而且存取速度也比软盘快得多

硬盘结构

硬盘结构包括:盘片磁头盘片主轴控制电机磁头控制器数据转换器接口缓存等几个部份。
在这里插入图片描述

  • 所有的盘片(一般硬盘里有多个盘片,盘片之间平行)都固定在一个主轴上。
  • 在每个盘片的存储面上都有一个磁头,磁头与盘片之间的距离很小(所以剧烈震动容易损坏),磁头连在一个磁头控制器上,统一控制各个磁头的运动。
  • 磁头沿盘片的半径方向动作,而盘片则按照指定方向高速旋转,这样磁头就可以到达盘片上的任意位置了。

  整个磁盘由多个盘面组成,固定在同一轴上,沿一个固定方向高速旋转,每个盘面包括上、下两个盘面,每个盘面用于存储信息,每个盘面有一个读写头,所有读写头都是固定在一起,同时同步移动的再一个盘面上读写头的轨迹,称为磁道,磁道就是磁面上的圆环,各个磁面上半径相同的词到总合成为一个柱面,每个磁道都别切分成很多扇形区域称为扇区
在这里插入图片描述

  • 磁盘的容量:磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数。

  • 磁头(head)数:每个盘片一般有上下两面,分别对应1个磁头,共2个磁头

  • 磁道(track)数:磁道是从盘片外圈往内圈编号0磁道,1磁道…,靠近主轴的同心圆用于停靠磁头,不存储数据

  • 柱面(cylinder)数:所有盘面上同半径的磁道数量

  • 扇区(sector)数:每个磁道都别切分成很多扇形区域,每道的扇区数量相同

  • 圆盘(platter)数:就是盘片的数量

在这里插入图片描述
扇区(sector):
  硬件(磁盘)上的最小的操作单位,是操作系统和块设备(硬件、磁盘)之间传送数据的单位。

物理块( block)
  block由一个或多个sector组成,是软件(OS、文件系统)中最小的操作单位;
块是上层软件中(操作文件时)使用的最小的操作单元,就是(操作文件时)一个块一个块进行操作(块的大小格式化时可以设置【如linux、fatfs等等】)

OS的虚拟文件系统从硬件设备上读取一个block,实际为从硬件设备读取一个或多个sector。

  • 对于文件管理来说,每个文件对应的多个block可能是不连续的;
  • block最终要映射到sector上,所以block的大小一般是sector的整数倍。
  • 不同的文件系统block可使用不同的大小,操作系统会在内存中开辟内存,存放block到所谓的block buffer中。

物理块是数据在磁盘上的存取单位,也就是每进行一次I/O操作,最小传输的数据大小。

  • 如果物理块定的比较大,比如一个柱面大小,这时,即使是1个字节的文件都要占用整个一个柱面,假设Linux环境下文件的平均大小为1K,那么分配32K的柱面将浪费97%的磁盘空间,也就是说,大的存取单位将带来严重的磁盘空间浪费。
  • 如果物理块过小,则意味着对一个文件的操作将进行更多次的寻道延迟和旋转延迟,因而读取由小的物理块组成的文件将非常缓慢

逻辑块
  具体文件系统管理的是一个逻辑空间,这个逻辑空间就象一个大的数组,数组的每个元素就是文件系统操作的基本单位逻辑块

  逻辑块是从0开始编号的,而且,逻辑块是连续的,与物理块相对应

硬盘上的数据定位

  每个扇区可存储128×2的N次方(N=0.1.2.3)字节的数据(一般为512B),扇区为数据存储的最小单元,外圈的扇区面积比内圈大,为何存储的数据量相同,这是因为内外圈使用的磁物质密度不同,但现在的硬盘已经采用内外圈同密度物质来存储数据了,以减少类似“大面积小数据”的浪费情况。

CHS模式:
  有了扇区(sector),有了柱面(cylinder),有了磁头(head),显然可以定位数据了,这就是数据定位(寻址)方式之一,CHS(也称3D),对早期的磁盘(上图所示)非常有效,知道用哪个磁头,读取哪个柱面上的第几扇区就OK了。

  CHS模式支持的硬盘容量有限,用8bit来存储磁头地址,用10bit来存储柱面地址,用6bit来存储扇区地址,而一个扇区共有512Byte,这样使用CHS寻址一块硬盘最大容量为256 * 1024 * 63 * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是8.4GB)

LBA编址方式:
  但现在很多硬盘采用同密度盘片,意味着内外磁道上的扇区数量不同,扇区数量增加,容量增加,3D很难定位寻址
新的寻址模式:LBA(Logical Block Addressing)。在LBA地址中,地址不再表示实际硬盘的实际物理地址(柱面、磁头和扇区)。

  LBA编址方式将CHS这种三维寻址方式转变为一维的线性寻址,它把硬盘所有的物理扇区的C/H/S编号通过一定的规则转变为一线性的编号,系统效率得到大大提高,避免了烦琐的磁头/柱面/扇区的寻址方式。

在访问硬盘时,由硬盘控制器再将这种逻辑地址转换为实际硬盘的物理地址。LBA下的编号,扇区编号是从0开始。

逻辑扇区号LBA的公式:
  LBA(逻辑扇区号)=磁头数 × 每磁道扇区数 × 当前所在柱面号 + 每磁道扇区数 × 当前所在磁头号 + 当前所在扇区号 – 1。

磁盘排序

  磁盘是直接存取设备,读取一个数据块的时间与当前读写头所处位置关系不大,所以可以通过读写数据块的次数来衡量存取时间

  对存放在磁盘中的文件进行排序属于典型的外排序,称为磁盘排序(disk sort),其磁盘排序过程与外排序所用归并排序过程基本相同

磁盘排序过程

在这里插入图片描述
  磁盘中的Fin文件存储着待排序的数据,通过计算讲Fin文件中的记录一部分一部分的调入内存处理,产生若干个文件F1…Fm,他们经过处理后都是有序的,称为顺串,然后再次将F1…Fm依次调入内存,通过相关归并算法生成Fout文件

实际上文件系统在读取时每次读取一个物理块,而一个物理块中可能有多个记录(数据),但为了方便起见以下每个记录占用一个物理块(读写记录只需要进行一次读写操作)

1.生成初始顺串

方法1(常规方法):

  按照内存大小将初始文件分为若干份( 我们假设需要排序的int数有12个,内存一次只能装下3个int数,就把12个数据分成4份(12/3=4),然后通过内排序处理成有序子串)
在这里插入图片描述

  • 根据给定文件记录数n,内存空间w,有初始归并个数m=[n/w](取上界)
  • 内存中排序的时候可以选择快速排序或归并排序等算法。
    在这里插入图片描述

方法2:置换-选择排序方法

  置换-选择排序方法生成初始归并段,该方法可以减少生成的初始归并段个数(初始归并段个数越少,则越接近最后排序结果,可以减少归并次数)

  1. 从待排文件Fin中按内存工作区WA的容量w读入w个记录。设归并段编号i=1。
  2. 从WA中选出关键字最小的记录Rmin。
  3. 将Rmin记录输出到当前归并段Fi。
  4. 若Fin不空,则从Fin中读入下一个记录x放在Rmin所在的工作区位置代替Rmin。
  5. 在工作区中所有≥Rmin的记录中选择出最小记录作为新的Rmin,重复(3),直到选不出这样的Rmin。
  6. 设i=i+1,开始一个新的归并段。
  7. 若工作区已空,则初始归并段已全部产生;否则执行(2)。

总结

  1. 共有n个记录,内存工作区WA的容量为w:

  2. 若在w个记录中选取最小关键字的采用简单比较方法,每次需要w-1次比较。

使用败者树可以提高比较效率

  1. 总的时间复杂度为O(nw)。

读出和写入均为n

2.处理顺串形成有序文件

1.多路平衡归并

什么是k路平衡归并:
  根据内存大小可以划分为不同的多路归并方案
  2路平衡归并:每一趟从m个归并段得到[m/2]个归并段。

处理时把两个小的有序子串合并成一个大的有序子串(二路归并)
在这里插入图片描述
多次使用归并算法,直到所有小文件都被处理完

总结:

  1. 每归并一次,参与归并的每个记录都要读一次和写一次。

  2. 总的读记录数(写记录数与之相同):

    • (F1+F2+F3+F4的记录数)×2=24=对起始12条记录读(写)了2遍=log2m

    • 该数越大,效率越差

    • 当m不满足2的i次方时,增加两个长度为0的虚段
      在这里插入图片描述

  3. 总的读记录数等同于哈夫曼树WPL(带权路径长度):

    • 从根结点到各个叶结点的路径长度与相应结点权值的乘积的和
    • 显然不同的归并方案所需要的读写记录数是不同的(使用二路归并或归并方案不同的二路归并、三路归并造成的读写次数也不同)
  4. 影响k路平衡归并的因素

    • 归并时需要读写磁盘的次数
        k路平衡归并时读写磁盘次数的计算:m=8,假设每个归并段4个记录:k=2
        读记录次数 = WPL = 8×4×3 = 96(如果每个记录占用一个物理块,读写磁盘次数=96×2=192次)
        采用k路平衡归并时,通常k越大,读写磁盘次数会减少。
    • 归并时需要关键字比较的次数
        k路平衡归并时关键字比较次数的计算
      在这里插入图片描述
        采用k路平衡归并时,则相应的归并树有[logkm]+1层,要对数据进行[logkm]趟扫描。
        总共需要的关键字比较次数为:
      在这里插入图片描述
      增大归并路数k,读写磁盘次数减少,而关键字比较次数会增大。若k增大到一定的程度,就会抵消掉由于减少读写磁盘次数而赢得的时间。
      在这里插入图片描述

2.利用败者树实现k路平衡归并过程

败者树
  败者树是一棵有k个叶子节点的完全二叉树(可以将大根堆看成胜者树,小根堆看成败者树),其中叶子节点存储参与归并的记录,分支节点存放关键字对应的段号。

  所谓败者是两个记录比较时关键字较大者,胜者是两个记录比较时关键字较小者

  败者树用于在k个记录中选取最小关键字的记录。败者树类似于堆排序中的小根堆。

利用败者树实现k路平衡归并的过程是:

利用败者数实现k路平衡归并的过程,是先建立败者树,然后对k个输入有序段进行k路平衡归并

一、先建立败者树

  建立败者数是采用类似于小根堆调整的方法实现的,初始时令所有的分支结点指向一个含最小关键字的叶子节点,然后从叶子结点出发,调整分支节点为新的败者(比较时关键字较大者)即可

  1. 计算树中结点个数:
      败者树是一棵有k个叶子节点的完全二叉树(结点个数最少),由二叉树性质,n2=n0-1=k-1,n=n0+n1+n2=2k-1+n1,为了让其结点最少,则让n1=0,n=2k-1(其中n为败者树中结点,下标为该结点的度数),另外添加一个冠军结点(存放数中最小者)。

例子,k=5:

首先构造非叶子节点n2=n0-1=k-1=4
在这里插入图片描述
每个叶子结点对应一个归并段,段号为0~4。
在这里插入图片描述
初始时每个分支结点取值“5(-∞)”,5表示段号(此时为虚拟段号,实际段号为0~4),- ∞表示最小关键字。
在这里插入图片描述

例如,某结点取值为“4(15)”,表示结点值来自4号段的关键字15对应的记录。

调整产生冠军(最小者)的过程

  从F4=>F0重复操作:将当前结点的关键字与父结点比较,将大的(败者)放在父结点中,小者(胜者)继续进行,直到根结点。最后将胜者放在冠军结点中。
在这里插入图片描述
二、用败者树进行归并

  1. 取每个输入有序段的第一个记录,作为败者树的叶子节点,建立初始败者数

    两两叶子节点进行比较,在双亲节点中存放比较的败者(关键字较大者),而让胜者去参加更高一层的比赛,如此在根节点之上胜出的冠军是所有关键字最小者

  2. 将胜出的记录(冠军)写至输出归并段,在对应的叶子节点处补充其输入有序段的下一个记录,若该有序段变空,则补充一个大关键字(比所有关键字都大,设为∞)的虚记录 ∞

  3. 调整败者树,选择新的冠军(最小的记录)

    从补充记录的叶子节点向上和双亲结点的关键字比较败者留在该双亲节点,胜者继续向上,直到树的根结点,最后将胜者放在根节点的双亲节点中 作为冠军

  4. 若胜出的记录关键字等于∞则归并结束,否则重复(2)

例子,归并过程:
  取出的冠军为1(5),从1号段中取下一个记录,沿着个分支向上操作,产生次小的记录。重复操作直到冠军为∞
在这里插入图片描述
总结

  1. 利用败者树实现k路平衡归并时,总共需要的关键字比较次数为:
    在这里插入图片描述

  2. 关键字比较次数与k无关 , 总的内部归并时间不会随k的增大而增大。

    只要内存空间允许,尽可能增大归并路数k。

  3. 采用败者树,置换-选择排序中关键字比较次数分析

共有n个记录,内存工作区WA的容量为w:若在w个记录中选取最小关键字的采用败者树方法,每次需要log2w次比较。总的时间复杂度为O(nlog2w)。

最佳归并树

  使用常规方法生成的初始归并段记录数是相同的,使用置换-选择排序方法生成的顺串可能会导致顺串与顺串中记录个数不同

  k路平衡归并适合初始归并段中的记录个数相同的情况,当初始归并段中的记录个数不同时哪些初始归并段先归并,哪些后归并都会影响算法的性能
在这里插入图片描述

若记录数多的先归并记录数少的归并段后归并,会导致该归并段参与的归并次数比记录少的归并段归并次数多,而读写次数计算和哈夫曼树WPL相同,从根结点到各个叶结点的路径长度与相应结点权值的乘积的和,可知算法效率显然不高

最佳归并树概念

  • 采用k叉哈夫曼树的构建顺序作为归并方案的归并树称为最佳归并树。
  • 因为使用哈夫曼树的构建顺序所以WPL最小
  • 并且在内存中归并时,利用败者树减少关键字比较次数可以使得性能更佳

存在的问题当进行k路归并时最后进行归并的归并段小于k个

(假设k=3)
在这里插入图片描述
解决的方法是加虚段(长度为0的段),每次恰好k个段进行归并
应加(k-1)-(m-1) Mod(%) (k-1)个虚段
在这里插入图片描述

构造步骤

  构建最佳归并树(m个初始归并段)就是构建带权路径长度最短的k叉(阶)哈夫曼树

一、计算是否需要增加虚短
  若x=(m-1) Mod (k-1)≠0,则需附加(k-1)-x个虚段,以使每次归并都可以对应k个段。

例子
在这里插入图片描述

  1. 初始归并段个数m=11,k=4。x=(m-1) Mod (k-1)=1≠0,因此需附加:
  2. (k-1)-x=2
  3. 个长度为0的虚段。

二、按照哈夫曼树的构造原则(权值越小的结点离根结点越远)构造最佳归并树。

(1)给定的n个权值[例:W1.W2.。。](这里的权值是归并段中的记录个数)构造n棵只有一个叶结点的叉树,从而得到一个二叉树的集合F-{T1… Ti.cum.}

(2)将二叉树集合F排序

(3)在F中选取四个长度最小的归并段,将作为叶子节点,其值累加和为其父节点值

(4)在森林F中再次选取3个归并段与叶子节点的父节点累加构造出一个父节点

(5)重复4直到只剩下F一棵树为止,这棵树就是最佳归并树

总结

  • 在满足k路平衡归并的前提,平衡归并树 ≡ 最佳归并树
    在这里插入图片描述

    两者之间的区别不过是归并方案不同

  • 由此选择归并方法时

    1. 满足k路平衡归并的前提(归并段中记录个数相同) => 采用平衡归并树;
    2. 否则 => 采用最佳归并树

磁带排序

磁带:属顺序存取设备。

磁带排序主要解决多个磁带交替使用的问题。

磁带排序也是外排序的一种,也使用多路归并排序

磁带多路平衡归并排序

  磁带多路平衡归并排序过程与磁盘的多路平衡归并排序过程基本上相同。

磁带排序和磁盘排序的主要不同之处在于磁带排序需要充分考虑归并段的分布状况。

磁带是顺序存取的,所以各归并段分布在不同磁带和同一磁带的不同位置对排序效率影响极大。

  1. 对输入文件的各段进行内排序,生成初始归并段。
  2. 把它们写到磁带上,然后再把这些归并段进行反复的归并,直到只剩下一个归并段(即为排好序的文件)为止。

磁带多阶段归并排序

  多阶段归并排序 属于多路非平衡归并排序,即各条带上的归并段不再保持平衡分布。

  使用磁带多阶段归并排序 是因为在进行磁带多路平衡归并排序时,重新分布有序段的问题需要使用的磁带数量是比较多的(k路归并时,需要使用2k个磁带,k个磁带做输入带,k个磁带做输出带)

  在k路多阶段归并中仅使用k+1条磁带,就可避免在多路平衡归并排序法中遇到的重新分布有序段的问题。

多阶段归并排序过程:

  1. 开始时,初始归并段不平衡地分配在前k条磁带上(输入带),第k+1条磁带作为输出带,开始为空。

    假设有17个初始归并段为(S1,S2,…,S17),用4台磁带机Tl、T2、T3和T4做三路的多阶段归并排序,其初始归并段的分布情况及排序过程中各磁带数据的变化情况。
    在这里插入图片描述

  2. 每一步归并只是部分记录参加,归并段最少的带在本步归并完成后便成为空带,作为下一步归并的输出带。
    在这里插入图片描述
    在这里插入图片描述

  3. 这样,k+1条磁带将轮流成为输出带,直到整个文件为一个排序文件为止。
    在这里插入图片描述
    总结

为了使归并的趟数达到最少,必须合理地分配各磁带上初始归并段的段数,归并段的总数以及在各带上的分布情况与k阶广义Fibonacci(斐波那契)序列有对应关系
在这里插入图片描述
初始归并段在各带上分布的段数应为:
在这里插入图片描述
k阶Fibonacci数列可用下面的递推公式导出:
在这里插入图片描述

  • 0
    点赞
  • 0
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

列队猫

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值