外部排序(败者树、置换-选择排序、最佳归并树)

news2024/11/13 22:27:30

外部排序可能会考查相关概念、方法和排序过程,外部排序的算法比较复杂,不会在算法设计上进行考查。

一、外部排序的基本概念与方法

外部排序指待排序文件较大,内存一次放不下,需存放在外存的文件的排序。

1. 基本概念

在许多应用中,经常需要对大文件进行排序,因为文件中的记录很多,无法将整个文件复制进内存中进行排序。因此,需要将待排序的记录存储在外存上,排序时再把数据一部分一部分地调入内存进行排序,在排序过程中需要多次进行内存和外存之间的交换。这种排序方法就称为外部排序

2. 方法

文件通常是按块存储在磁盘上的,操作系统也是按块对磁盘上的信息进行读写的。因为磁盘读/写的机械动作所需的时间远远超过内存运算的时间(相比而言可以忽略不计),因此在外部排序过程中的时间代价主要考虑访问磁盘的次数,即 I/O 次数。

外部排序通常采用归并排序法。它包括两个阶段:
① 根据内存缓冲区大小,将外存上的文件分成若干长度为 t 的子文件,依次读入内存并利用内部排序方法对它们进行排序,并将排序后得到的有序子文件重新写回外存,称这些有序子文件为 归并段或顺串
② 对这些归并段进行逐趟归并,使归并段(有序子文件)逐渐由小到大,直至得到整个有序文件为止。

二、多路平衡树(Multiway Balanced Tree)与败者树(Loser Tree)

为减少平衡归并中外存读写次数所采取的方法:增大归并路数和减少归并段个数。利用败者树增大归并路数。

1. 多路平衡树的算法思想

多路平衡树是一种树数据结构,它允许每个结点有多个子结点,通常用于数据库和文件系统中。它的目的是保持数据的平衡、减少搜索时间,并提高插入和删除操作的效率。

2. 多路平衡树的示例

一般地,对 r 个初始归并段,做 k 路平衡归并(即每趟将 k 个或 k 个以下的有序子文件归并成一个有序子文件)。第一趟可将 r 个初始归并段归并为 ⌈r / k⌉ 个归并段,以后每趟归并将 m 个归并段归并成 ⌈m / k⌉ 个归并段,直至最后形成一个大的归并段为止。树的高度 - 1 = ⌈logkr⌉ = 归并趟数 S 。可见,只要增大归并路数 k,或减少初始归并段个数 r,都能减少归并趟数 S,进而减少读写磁盘的次数,达到提高外部排序速度的目的。

增加归并路数 k 能减少归并趟数 S,进而减少 I/O 次数。然而,增加归并路数 k 时,内部归并的时间将增加。做内部归并时,在 k 个元素中选择关键字最小的记录需要比较 k - 1 次。每趟归并 n 个元素需要做 (n - 1) × (k - 1) 次比较, S 趟归并总共需要的比较次数为:

S × (n - 1) × (k - 1) = ⌈logkr⌉ × (n - 1) × (k - 1) = ⌈log2r⌉ × (n - 1) × (k - 1) / ⌈log2k⌉。

式中,(k - 1) / ⌈log2k⌉ 随着 k 的增长而增长,因此内部归并时间亦随k 的增长而增长。这将抵消由于增大 k 而减少外存访问次数所得到的效益。因此,不能使用普通的内部归并排序算法。为了使内部归并不受 k 的增大的影响,引入了 败者树

3. 败者树的算法思想

败者树是一种用于高效合并 k 个有序序列的完全二叉树。它由 k 个外部结点和 k - 1 个内部结点构成,每一个内部结点记录的是在该结点比赛的输者,根结点保存的是“赢家”。

4. 败者树的示例

败者树是树形选择排序的一种变体,可视为一棵完全二叉树。k 个叶结点分别存放 k 个归并段在归并过程中当前参加比较的记录,内部结点用来记忆左右子树中的“失败者”,而让胜者往上继续进行比较,一直到根结点。若比较两个数,大的为失败者、小的为胜利者,则根结点指向的数为最小数。

因为 k 路归并的败者树深度为 ⌈log2k⌉,因此 k 个记录中选择最小关键字,最多需要 ⌈log2k⌉ 次
比较。所以总的比较次数为:

S × (n - 1) × ⌈log2k⌉ = ⌈logkr⌉ × (n - 1) × ⌈log2k⌉ = ⌈log2r⌉ × (n - 1) 。

可见,使用败者树后,内部归并的比较次数与 k 无关了。因此,只要内存空间允许,增大归并路数 k 将有效地减少归并树的高度,从而减少 I / O 次数,提高外部排序的速度。

值得说明的是,归并路数 k 并不是越大越好。归并路数K 增大时,相应地需要增加输入缓冲区的个数。若可供使用的内存空间不变,势必要减少每个输入缓冲区的容量,使得内存、外存交换数据的次数增大。当K 值过大时,虽然归并趟数会减少,但读写外存的次数仍会增加。

5. 例题

① 多路平衡归并的作用是( A )。
A. 减少归并趟数
B. 减少初始归并段的个数
C. 便于实现败者树
D. 以上都对

② 在下列关于外部排序过程输入/输出缓冲区作用的叙述中,不正确的是( D )。
A. 暂存输入/输出记录
B. 内部归并的工作区
C. 产生初始归并段的工作区
D. 传送用户界面的消息

③ 在由 k 路归并构建的败者树中选取一个关键字最小的记录,则所需时间为( C )。
A. O(1)
B. O(k)
C. O(logk)
D. 以上都不对

④ 下列关于小顶堆和败者树的说法中,正确的是( C )。
I. 败者树从下往上维护,每上一层,只需和失败结点比较 1 次
II. 败者树的每次维护,必定要从叶结点一直走到根结点,不可能从中间停止
III. 堆从上往下维护,每下一层,若其左右孩子均不为空,则需比较 2 次
IV. 堆的每次维护,必定要从根结点一直走到叶结点,不可能从中间停止
A. I、III
B. II、III
C. I、II、III
D. I、III、IV

三、置换-选择排序(生成初始归并段)(Replacement Selection Sort)

利用置换-选择排序增大归并段长度来减少归并段个数。

1. 算法思想与实现步骤

1)算法思想:
由上述讨论可知,减少初始归并段个数 r 也可以减少归并趟数 S 。若总的记录个数为 n,每个归并段的长度为 t,则归并段的个数 r = ⌈n / t⌉ 。采用内部排序方法得到的各个初始归并段长度都相同(除最后一段外),它依赖于内部排序时可用内存工作区的大小。因此,必须探索新的方法,用来产生更长的初始归并段,这就是下面要介绍的置换-选择算法。

2)实现步骤:
设初始待排文件为 FI,初始归并段输出文件为 FO,内存工作区为 WA,FO 和 WA 的初始状态为空, WA 可容纳 w 个记录。
① 从 FI 输入 w 个记录到工作区 WA 。
② 从 WA 中选出其中关键字取最小值的记录,记为 MINIMAX 记录。
③ 将 MINIMAX 记录输出到 FO 中去。
④ 若 FI 不空,则从 FI 输入下一个记录到 WA 中。
⑤ 从 WA 中所有关键字比 MINIMAX 记录的关键字大的记录中选出最小关键字记录,作为新的 MINIMAX 记录。
⑥ 重复 ③-⑤,直至在 WA 中选不出新的 MINIMAX 记录为止,由此得到一个初始归并段,输出一个归并段的结束标志到 FO 中去。
⑦ 重复 ②-⑥,直至 WA 为空。由此得到全部初始归并段。
【注:上述算法,在 WA 中选择 MINIMAX 记录的过程需利用败者树来实现。】

2. 示例

3. 例题

① 置换-选择排序的作用是( A )。
A. 用于生成外部排序的初始归并段
B. 完成将一个磁盘文件排序成有序文件的有效的外部排序算法
C. 生成的初始归并段的长度是内存工作区的 2 倍
D. 对外部排序中输入/归并/输出的并行处理

② 一个无序文件的 n 个记录采用置换-选择排序产生 m 个有序段,则 m 和 n 的关系是( D )。
A. m 与 n 成正比
B. m = log2n
C. m 与 n 成反比
D. 以上都不对

四、最佳归并树(Optimal Merge Tree)

由长度不等的归并段,进行多路平衡归并,需要构造最佳归并树。

1. 算法思想

文件经过置换-选择排序后,得到的是长度不等的初始归并段。在归并树中,让记录数少的初始归并段最先归并,记录数多的初始归并段最晚归并,就可以建立总的 I / O 次数最少的最佳归并树。(将哈夫曼树的思想推广到 m 叉树,以优化归并树的 WPL 。)

若初始归并段不足以构成一棵严格 k 叉树(也称正则 k 叉树)时,需添加长度0 的“虚段“,按照哈夫曼树的原则,权为 0 的叶子应离树根最远。那么如何判定添加虚段的数目?

正则 k 叉树:树中每个分支结点都有 k 个孩子,即树中只有度为 0 或 k 的结点。

设度为 0 的结点有 n0 个,度为 k 的结点有 nk 个,归并树的结点总数为 n,则有:

  • n = nk + n0(总结点数 = 度为 k 的结点数 + 度为 0 的结点数)
  • n = k × nk + 1(总结点数 = 所有结点的度数之和 + 1)

因此,对严格 k 叉树有 n0 = (k - 1) × nk + 1,由此可得 nk = (n0 - 1) / (k - 1) 。

  • 若 (n0 - 1) % (k - 1) = 0(% 为取余运算),则说明这 n0 个叶结点(初始归并段)正好可以构造 k 叉归并树。此时,内结点有 nk 个。
  • 若 (n0 - 1) % (k - 1) = u != 0,则说明对于这 n0 个叶结点,其中有 u 个多余,不能包含在 k 叉归并树中。为构造包含所有 n0 个初始归并段的 k 叉归并树,应在原有 m 个内结点的基础上再增加 1 个内结点。它在归并树中代替了一个叶结点的位置,被代替的叶结点加上刚才多出的 u 个叶结点,即再加上 k - u - 1 个空归并段,就可以建立归并树。

2. 示例

下图左侧为 3 路平衡归并的归并树,右侧为 3 路平衡归并的最佳归并树

3. 例题

① 最佳归并树在外部排序中的作用是( B )。
A. 完成 m 路归并排序
B. 设计 m 路归并排序的优化方案
C. 产生初始归并段
D. 与锦标赛树的作用类似

② 在由 m 个初始归并段构建的 k 阶最佳归并树中,不需要补充虚段,则度为 k 的结点个数是( C)。
A. (m - 1) / k
B. m / k
C. (m - 1) / (k - 1)
D. 无法确定

③ 【2013 统考真题】已知三叉树 T 中 6 个叶结点的权分别是 2, 3, 4, 5, 6, 7, T 的带权(外部)路径长度最小是( B )。
A. 27
B. 46
C. 54
D. 56

④ 【2019 统考真题】设外存上有 120 个初始归并段,进行 12 路归并时,为实现最佳归并,需要补充的虚段个数是( B )。
A. 1
B. 2
C. 3
D . 4

五、赢者树(Winner Tree)【拓展】

1. 基本概念

有 n 个选手的一棵嬴者树是一棵完全二叉树,它有 n 个外部结点和 n - 1 个内部结点,每个内部结点记录的是在该结点比赛的嬴者,根结点保存的是所有输入序列中的最小(或最大)元素。

为了确定一场比赛的赢者树,我们假设每个选手都有一个分数,而且有一个规则用来比较两个选手的分数以确定赢者。

  • 在最小赢者树(min winner tree)中,分数小的选手获胜。
  • 在最大赢者树(max winner tree)中,分数大的选手获胜。
  • 在分数相等,即平局的时候,左孩子表示的选手获胜。

赢者树的一个优点在于:当一名选手的分数改变时,修改竞赛树比较容易。在一棵 n 个选手的赢者树中,当一个选手的分数发生变化时,需要修改的比赛场次介于 1 到 ⌈log2n⌉ 之间,因此,赢者树的重构需耗时 O(logn) 。此外, n 个选手的赢者树可以在 Θ(n) 时间内初始化,方法是沿着从叶子到根的方向,在内部结点进行 n - 1 场比赛。也可以采用后序遍历来初始化,每访问一个结点,就进行一场比赛。

2. 应用

1)排序

可以用一棵最小赢者树,用时 Θ(nlogn) 对 n 个元素排序。

① 首先,用 n 个元素代表 n 名选手对赢者树进行初始化。关键字决定每场比赛的结果,总冠军是关键字最小的元素。将该元素的关键字改为最大值(如∞),使它赢不了其他任何选手。

② 然后重构赢者树,以反映出该元素的关键字的变化。这时的总冠军是按序排在第二的元素。将该元素的关键字改为∞ ,再一次重构赢者树。这时的总冠军是按序排在第三的元素。以此类推,可以完成 n 个元素的排序。

③ 赢者树初始化的用时为 Θ(n) 。每次改变赢者的关键字并重构赢者树的用时为 Θ(logn),因为在从一个外部结点到根的路径上,所有的比赛需要重赛。赢者树的重构共需 n - 1 次。整个排序过程的时间 Θ(n + nlogn) = Θ(nlogn) 。

2)生成初始归并段

内部排序法(internal sorting method)要求待排序的元素全部放入计算机内存。但是,当待排序的元素所需要的空间超出内存的容量时,内部排序法就需要频繁地访问外部存储介质(如磁盘),那里存储着部分或全部待排的元素。这使得排序效率大打折扣。于是我们需要引入外部排序法(external sorting method)。外部排序一般包括两个步骤:
① 生成一些初始归并段,每一个初始归并段都是有序集;
② 将这些初始归并段合并为一个归并段。

在归并段合并中,决定时间的因素之一是在步骤 ① 中生成的初始归并段的个数。使用赢者树可以减少初始归并段的个数。假设一棵赢者树有 p 名选手,其中每个选手是输入集合的一个元素,它有一个关键字和一个归并段号。初始时这 p 个元素的归并段号均为 1 。当两个选手进行比赛时,归并段号小的选手获胜。在归并段号相同时,关键字小的选手获胜。

为生成初始归并段,重复地将总冠军 W 移到它的归并号所对应的归并段,并用下一个输入元素 N 取代 W。

  • 如果 N 的关键字大于等于 W 的关键字,则令元素 N 的归并段号与 W 的相同,因为在 W 之后把 N 输出到同一个归并段不会影响归并段的次序。
  • 如果 N 的关键字小于 W 的关键字,则令元素 N 的归并段号为 W 的归并段号加 1,因为在 W 之后把 N 输出同一个归并段将破坏归并段的排序。

当采用上述方法生成初始归并段时,初始归并段的平均长度约为 2 × p 。

3)k路合并

合并 k 个归并段的简单方法是:从 k 个归并段的前面,不断把关键字最小的元素移到正在生成的输出归并段。当所有元素从 k 个输入归并段移至输出归并段时,合并过程就完成了。

在 k 路合并中,k 个归并段合并成一个归并段。按照上述方法,每一个元素合并到输出归并段所需时间为 O(k),因为每一次迭代都需要在 k 个关键字中找到最小值。因此,产生一个大小为 n 的归并段所需要的总时间为 O(k × n) 。

而使用赢者树可将这个时间缩短为 Θ(k + nlogk)。首先用 Θ(k) 的时间初始化一棵有 k 个选手的赢者树,这 k 个选手分别是 k 个归并段的头元素。然后将赢者移入输出归并段,并从相应的输入归并段中取出下一个元素替代赢者的位置。若该输入段无下一个元素,则用一个关键字值很大(不妨为∞)的元素替代。这个提取和替代赢家的过程需要 n 次,一次需要时间为 Θ(logk) 。一次 k 路合并的总时间为 Θ(k + nlogk) 。

4)赢者树与败者树

在许多应用中,只有在一个新选手替代了前一个赢者之后,才执行重新组织比赛的操作。这时,在从赢者的外部结点到根结点的路径上,所有比赛都要重新进行。如果每个内部结点记录的是在该结点比赛的输者而不是赢者,那么当赢者改变后,在从该结点到根的路径上,重新确定每一场比赛的选手所需要的操作量就可以减少。

当一个赢者发生变化时,使用败者树可以简化重赛的过程。但是,当其他选手发生改变时,就不是那么回事了。因此,仅当发现变化的选手为前次比赛的赢家时,对于重新组织比赛的操作,采用败者树比采用赢者树执行效率更高。

赢者树:保留胜利者,便于快速提取当前最优元素,并在合并时保持高效。败者树:保留被淘汰者,在更新时具有一定的灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2039093.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python入门之命令提示符和文本创建.py文件

1.命令提示符 程序 快捷键:windowsR 在安装完python以后,可以直接在命令提示符程序上敲代码进行初步尝试。 python解释器 计算机是不认识python代码的,计算机只能识别0和1这个二进制的数,所以需要一个翻译官“python翻译器”。 …

【Linux系列】known_hosts详解

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【Linux网络(一)】Socket编程

文章目录 1. 预备知识1.1 认识端口号1.2 初识TCP协议1.3 初识UDP协议1.4 网络字节序1.5 socket编程接口1.5.1 套接字编程的种类1.5.2 sockaddr结构体1.5.3 socket 常见API1.5.4 地址转换函数 2. 编写UDP服务器与客户端2.1 UDP服务器的创建2.2 UDP服务器接收/发送数据2.3 补充知…

动态规划——背包问题(01背包、完全背包,分组背包与二进制优化)

本蒟蒻写二进制优化开始的时候写昏了,并且昏了一下午。但好在有神犇救命,这篇博客才得以面世——躲着人群 一、01背包 概述: 其常见的问题形式为:给出n个物品,每个物品有对应的价值和体积。给出背包容量后求不超过背…

硬件开发流程

1.看原理图找引脚 --开发板上找到LED,查看丝印 --在原理图中根据丝印找到对应的器件 --找到对应的引脚 2.配置引脚功能(对应硬件的工作原理) 3.控制对应的引脚(或控制器) volatile: 易失性修饰符

qtpdfium 多平台编译

源码下载地址:https://codeload.github.com/kkzi/qpdf/zip/2681018e300738d6da9a9f89f06c93fc3ef17831 参考:https://blog.51cto.com/u_2194662/5256871 开发环境:QT 5.15.2 1. windows下编译: 编译环境:vs2019qt5.15…

【Python】线性规划模型(笔记)

线性规划的作用 求一个线性目标函数在线性可行域内的最值问题 线性规划的典型应用 配送运输问题:选大车还是小车生产规划问题:每种原料各买多少几何切割问题:切割长宽各多少买卖利润问题:最多能挣多少钱… 线性规划的本质 问…

6 二进制、十进制、八进制和十六进制的概念与进制转换,原码、反码、补码的概念与计算

目录 1 进制 1.1 常见的进制 1.2 不同进制的整数在 C 语言中的写法 1.3 printf 以不同进制形式输出整数 1.4 案例:使用不同的进制形式输出同一整数 2 进制的转换 2.1 二进制与十进制转换 2.1.1 二进制转换成十进制 2.1.2 十进制转换成二进制 2.2 八进制和…

pygame 赛亚人打怪

笔者最近学了一点pygame,献丑了。 代码附上,大佬勿喷! import random import timeimport pygameWIDTH 410 HEIGHT 750 ENEMY_BORN pygame.USEREVENT 1 BULLET_BORN pygame.USEREVENT 10 ENEMY_BULLET_BORN pygame.USEREVENT 100 REW…

2024年最新版小程序云开发数据模型的开通步骤,支持可视化数据库管理,支持Mysql和NoSql数据库,可以在vue3前端web里调用操作

小程序官方又改版了,搞得石头哥不得不紧急的再新出一版,教大家开通最新版的数据模型。官方既然主推数据模型,那我们就先看看看新版的数据模型到底是什么。 一,什么是数据模型 数据模型是什么 数据模型是一个用于组织和管理数据的…

Linux信号控制进程种类、内存查看和NICE优先级

1.信号种类 1)SIGHUP 重新加载配置 2)SIGINT 键盘中断CtrlC 3)SINGQUIT 键盘退出Ctrl\ 9)SIGKILL 强制终止 15)SIGTERM 正常结束终止 18)SIGCONT 继续 19)SIGSTOP…

使用克隆软件克隆Windows 10 硬盘

为什么 Windows 用户要克隆硬盘驱动器 您打算将旧硬盘升级为新硬盘吗?是否可以将操作系统迁移到 SSD?如何制作硬盘的相同副本?如何安全地升级操作系统而不会出现可启动故障问题?是否有任何安全便捷的方法可用于传输数据并升级到更…

Linux 中断机制(二)之中断子系统框架

目录 一、概述二、中断控制器 GIC1、Distributor 接口2、Redistributor 接口3、CPU 接口 三、中断类型四、中断号五、中断申请函数1、request_irq 函数2、request_threaded_irq() 函数3、gpio_to_irq 函数4、free_irq 函数 六、中断服务函数 一、概述 一个完整的中断子系统框架…

zdppy+vue3+onllyoffice开发文档管理系统实战 20240814上课笔记 验证码功能的进一步优化

遗留问题 1、实现验证码的功能2、要记录登录的Token和用户名,跳转到首页3、注册功能4、用户管理5、角色管理6、权限管理7、分享功能 当前进度 目前我们已经封装了zdppy_captcha这个框架,这个框架是专门用来生成验证码的。 缺少一些功能: …

024集——批量复制文字样式、改变文本文字样式——vba代码实现

CAD vba 不可直接修改文字样式的名称,可复制文字样式,文字样式名称前加特定前缀 要为对象改变文字样式,可使用 StyleName 属性。 If ent.ObjectName "AcDbText" Then ent.StyleName "新的" Set sel creatsel("…

【C#】读取与写入txt文件内容

在 C# 中读取和写入文本文件内容是一个常见的任务。以下是使用几种不同方法读取和写入文本文件的示例。 一、读取txt文件内容 1.1 使用 StreamReader using System; using System.IO;class Program {static void Main(){string filePath "C:\path\to\your\file.txt&qu…

【Unity打包Android】Gradle报错,Deprecated Gradle features were used in this build ···

Unity 2020.3.41f1c1 打包Android 加入Google Admob广告SDK后,打包Android失败,具体报错如下: 报错1: Starting a Gradle Daemon, 2 stopped Daemons could not be reused, use --status for details> Configure project :l…

Hutool找出对象修改前后的字段变化

首先Maven引入Hutool依赖&#xff1a; <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.21</version> <!-- 请根据实际最新版本调整 --></dependency> 测试&#xff1a; 1…

OpenAI API error: “Unrecognized request argument supplied“

题意&#xff1a;OpenAI API 错误&#xff1a;‘提供了无法识别的请求参数’ 问题背景&#xff1a; Im receiving an error when calling the OpenAI API. Its not recognizing file argument, which I submitted to the API. 我在调用 OpenAI API 时遇到错误。API 不识别我提…

python语言day6 os random datetime .ini文件

os&#xff1a; 获取运行当前py文件的绝对路径&#xff1a; abspath中添加路径&#xff0c;会直接和绝对路径拼接。 folder_path os.path.abspath("") print(folder_path) 路径拼接&#xff1a; mac系统路径&#xff1a;file/TranslucentTB win系统路径&#xff1a;…