考研复习之数据结构笔记(十八)排序(下)(外部排序:基本方法、多路平衡归并、置换-选择排序、最佳归并树)

目录

一、外部排序基本方法

二、多路平衡归并

三、置换-选择排序

四、最佳归并树


一、外部排序基本方法

我们一般提到排序都是指内排序,比如快速排序,堆排序,归并排序等,所谓内排序就是可以在内存中完成的排序。RAM的访问速度大约是磁盘的25万倍,我们当然希望如果可以的话都是内排来完成。但对于大数据集来说,内存是远远不够的,这时候就涉及到外排序的知识了。

外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。

假设文件有4500个记录,每块磁盘可以放75个记录,计算机中用于排序的内存区可以存放 450 个记录,试问:
1)可以建立多少个初始归并段?每个归并段有多少个记录?存放于多少个块中?
2)应采用几路归并,请写出每趟需要读写磁盘的块数

解答:
1) 文件中有4500个记录,内部排序区可容纳450个记录(其实排序过程是 依次 输入1-450,451-900… 进行排序,然后输出构成有序的初始归并段 ),则可建立的初始归并段为4500/450 = 10,每个初始归并段有450个记录,存放于 450/75 =6 个块中。
2)内存区可以容纳6个块,所以可以建立5个输入缓冲区。1个输出缓冲区,因此采用5路归并。
归并次数为 log(5)10 = 2,每次归并需要读写磁盘次数都为4500/75=60 次 ,每次归并需要读写磁盘总次数为为60 * 2 =120 次,做了两趟归并,读写总次数 2 * 120=240。

2.外部排序总时间= 内部排序所需时间+外村信息读写时间+内部归并需要时间

内部排序所需时间 :就是第一次进行生成初始归并序列的时间,这其中就有一次读写时间,排序时间可以忽略。如上题内部排序所需时间为 120 相当于一次归并的时间
外存信息读写时间 :其实就是归并次数乘以每次读写时间,上题为两次归并,每次归并读写磁盘次数为120 ,所以总时间为2 * 120 = 240次。
 

二、多路平衡归并

假设有一个含10000 个记录的文件,首先通过10 次内部排序得到10 个初始归并段R1~R10 ,其中每一段都含1000 个记录。然后两两归并,直至得到一个有序文件为止

如R1与R2归并排序成R12,R3与R4排序成R34,再R12与R34排序从R1234

由于多路归并,有k路,就要比较k-1次,所以有了减少比较次数的胜者树与败者树,而多路归并常用败者树

多路归并排序算法在常见数据结构书中都有涉及。从2路到多路(k路),增大k可以减少外存信息读写时间,但k个归并段中选取最小的记录需要比较k-1次, 为得到u个记录的一个有序段共需要(u-1)(k-1)次,若归并趟数为s次,那么对n个记录的文件进行外排时,内部归并过程中进行的总的比较次数为 s(n-1)(k-1),也即(向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),而(k- 1)/log2k随k增而增因此内部归并时间随k增长而增长了,抵消了外存读写减少的时间,这样做不行,由此引出了“败者树”tree of loser的使用。在内部归并过程中利用败者树将k个归并段中选取最小记录比较的次数降为(向上取整)(log2k)次使总比较次数为(向上取整) (log2m)(n-1),与k无关。

 

图2-1 五路归并的败者树

败者树是完全二叉树, 因此数据结构可以采用一维数组。其元素个数为k个叶子结点、k-1个比较结点、1个冠军结点共2k个。ls[0]为冠军结点,ls[1]--ls[k- 1]为比较结点,ls[k]--ls[2k-1]为叶子结点(同时用另外一个指针索引b[0]--b[k-1]指向)。另外bk为一个附加的辅助空间,不 属于败者树,初始化时存着MINKEY的值。

多路归并排序算法的过程大致为:

1):首先将k个归并段中的首元素关键字依次存入b[0]--b[k-1]的叶子结点空间里,然后调用CreateLoserTree创建败者树,创建完毕之后最小的关键字下标(即所在归并段的序号)便被存入ls[0]中。然后不断循环:

2)把ls[0]所存最小关键字来自于哪个归并段的序号得到为q,将该归并段的首元素输出到有序归并段里,然后把下一个元素关键字放入上一个元素本来所 在的叶子结点b[q]中,调用Adjust顺着b[q]这个叶子结点往上调整败者树直到新的最小的关键字被选出来,其下标同样存在ls[0]中。循环这个 操作过程直至所有元素被写到有序归并段里。

败者树

我们根据树形选择排序的思想可以建立一棵败者树。败者树就是对于分支节点存两个子树中的败者(对于本处败着的定义就是两个数字中较大的那个的下标索引),对于树的根节点再定义一个双亲结点,表示整个树的胜者。(对于这边就是整个树种最小的数的下标索引)

注意这边要区别于树形选择排序的竞赛树,或者也可以称位胜者树。


流程

每次从根结点的双亲结点获取到当前的最小值,然后把该叶子结点处的所在段取下一个数字,并更新从这个位置开始到达根部的所有结点。

如果该当前元素大于父节点的元素,也就是比上一轮的『败者』更劣,则上轮的败者作为该子树的胜者继续往上更新,而本元素则更新为本子树的败者也就是当前父节点的值。

对于败者树还有这样的理解:每个分支结点表示两个子树中胜者中对抗后失败的结点。类比到比赛中,相当于两个队伍从各自的分区获胜晋级上来了,对抗后赢的队伍还要继续往上晋级,而失败的队伍就结束了,对此进行颁奖表彰它到目前的成绩,也就是在当前结点记录这个败者。
 

三、置换-选择排序

算法思想:

选择内存缓冲区中的一个数,该数需要符合以下的条件:

    该数必须大于当前初始归并段中任意数字
    该数是符合条件1的可选数中最小的一个

如果符合上述条件,则将该数加入当前初始归并段,直到内存缓冲区中的所有记录都比当前初始归并段最大的记录小时,就生成了一个初始归并段。重复上述过程,生成多个初始归并段,直到文件的所有记录都被归入某个初始归并段。

其具体步骤如下:

  1. 首先从初始文件中输入 l 个记录到内存工作区中;
  2. 从内存工作区中选出关键字最小的记录,将其记为 MINIMAX 记录;
  3. 将 MINIMAX 记录输出到归并段文件中;
  4. 此时内存工作区中还剩余 l-1 个记录,若初始文件不为空,则从初始文件中输入下一个记录到内存工作区中;
  5. 从内存工作区中的所有比 MINIMAX 值大的记录中选出值最小的关键字的记录,作为新的 MINIMAX 记录;
  6. 重复过程 3—5,直至在内存工作区中选不出新的 MINIMAX 记录为止,由此就得到了一个初始归并段;
  7. 重复 2—6,直至内存工作为空,由此就可以得到全部的初始归并段。

 图3-1 过程实例

四、最佳归并树

算法思想:与哈夫曼树完全一致。

例如,现有通过置换选择排序算法所得到的 9 个初始归并段,其长度分别为:9,30,12,18,3,17,2,6,24。在对其采用 3-路平衡归并的方式时可能出现如图所示的情况:

图4-1 三路平衡归并树

图中的叶子结点表示初始归并段,各自包含记录的长度用结点的权重来表示;非终端结点表示归并后的临时文件。

假设在进行平衡归并时,操作每个记录都需要单独进行一次对外存的读写,那么图 1 中的归并过程需要对外存进行读或者写的次数为:

(9+30+12+18+3+17+2+6+24)*2*2=484(图中涉及到了两次归并,对外存的读和写各进行 2 次)

从计算结果上看,对于图中的 3 叉树来讲,其操作外存的次数恰好是树的带权路径长度的 2 倍。所以,对于如何减少访问外存的次数的问题,就等同于考虑如何使 k-路归并所构成的 k 叉树的带权路径长度最短。

若想使树的带权路径长度最短,就是构造赫夫曼树。

在学习哈夫曼树时,只是涉及到了带权路径长度最短的二叉树为赫夫曼树,其实扩展到一般情况,对于 k 叉树,只要其带权路径长度最短,亦可以称为哈夫曼树。

若对上述 9 个初始归并段构造一棵哈夫曼树作为归并树,如图所示:

图4-2 哈夫曼树作为3-路归并树


依照图 2 所示,其对外存的读写次数为:

(2*3+3*3+6*3+9*2+12*2+17*2+18*2+24*2+30)*2=446

通过以构建哈夫曼树的方式构建归并树,使其对读写外存的次数降至最低(k-路平衡归并,需要选取合适的 k 值,构建哈夫曼树作为归并树)。所以称此归并树为最佳归并树。

附加“虚段”的归并树

上述图 4-2 中所构建的为一颗真正的 3叉树(树中各结点的度不是 3 就是 0),而若 9 个初始归并段改为 8 个,在做 3-路平衡归并的时候就需要有一个结点的度为 2。

对于具体设置哪个结点的度为 2,为了使总的带权路径长度最短,正确的选择方法是:附加一个权值为 0 的结点(称为“虚段”),然后再构建赫夫曼树。例如图 2 中若去掉权值为 30 的结点,其附加虚段的最佳归并树如图 4-3 所示:

图 4-3 附加虚段的最佳归并树

注意:虚段的设置只是为了方便构建赫夫曼树,在构建完成后虚段自动去掉即可。

对于如何判断是否需要增加虚段,以及增加多少虚段的问题,有以下结论直接套用即可:

在一般情况下,对于 k–路平衡归并来说,若 (m-1)MOD(k-1)=0,则不需要增加虚段;否则需附加 k-(m-1)MOD(k-1)-1 个虚段。
 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

D了一天bug忘了编译

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

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

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

打赏作者

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

抵扣说明:

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

余额充值