北理工《数据结构》本科教学课件10 外部排序
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
y 因为tIO >> tmg,想要提高外排序的速度,应减少 d. y d和归并过程的关系:
归并路数 k 总读写磁盘次数 d 归并趟数s
2
500
4
5
300
2
10
200
1
为了减小d,应该减小s。 s = ⎡logkm⎤
减小总读写次数 d的途径:增加归并路数k或减小初始段数m。
tES = m*tIS + d*tIO + s*u*tmg
8
10.2 多路平衡归并排序
y 做内部 k 路归并时,在 k 个对象中选择最小者,需要 顺序比较 k-1 次。
y 每趟归并 u 个对象需要做(u-1)*(k-1)次比较,s趟归并 总共需要的比较次数为:
s(k −1)(n −1) = ⎣logk m⎦(k −1)(n −1)
= log2 m (k −1)(n −1) log2 k
方法:将bi与其父节点指示的 上次比较的失败者相比.
ls[2] 53
ls[0] 55 ls[1] 55
创建败者树: 插入b3(6)
5 ls[3]
b5 -∞
ls[4] 44
b0 10
b3 6
b4 12
10 15
b1 9
9 18
6 15
12 37
… ∞
… ∞
… Run3 ∞
…
Run0
Run1
∞
b2 20
11
败者树
y 败者树是一棵正则的完全二叉树,其中 y 每个叶结点存放各归并段在归并过程中当前 参加比较的对象; y 每个非叶结点记忆它两个子女结点中对象关 键码大的结点(即败者);
typedef int LoserTree[k]; typedef struct{
KeyType key; } ExNode, External[k+1];
随k增长而增长, 增大k,会使得归
=
(k −1) log2 k
log2 m(n −1)
办法:减小k
并的时间增大 个对象中选最小的比较次数
减小总读写磁盘次数 d的途径1:增加归并路数k
9
败者树
y 使用“败者树”从 k 个归并段中选最小者,当 k 较大时 (k ≥ 6),选出关键码最小的对象只需比较 ⎡log2k⎤ 次。
46, 38, 29, 14, 61, 15, 30, 1, 48, 52, 3, 63, 27, 4, 13, 89, 24, 46, 58, 33, 76} y 假设内存工作区WA可容纳6个记录,得:
创建败者树: 插入b1(9)
2 ls[3]
b5
ls[4] 4
b0 10 b1 99
10
9
-∞ b3 6
b4 12
15
18
6 15
12 37
… ∞
… ∞
… Run3 ∞
…
Run0
Run1
∞
b2 20
20 22 … ∞
Run2
20
败者树-创建
3)依次从b[k-1],b[k2],…,b[0]出发调整失败者; 方法:将bi与其父节点指示的 上次比较的失败者相比.
ls[2] 30
ls[0] 53 ls[1] 11
创建败者树: 插入b0(10)
2 ls[3]
b5
ls[4] 4
b0 1100 b1 9
10
9
-∞ b3 6
b4 12
15
18
6 15
12 37
… ∞
… ∞
Baidu Nhomakorabea
… Run3 ∞
…
Run0
Run1
∞
b2 20
20 22 … ∞
Run2
21
败者树-创建
//创建初始失败者树的算法 void CreateLoserTree ( LoserTree &ls) {//已知b[0]到b[k-1]为完全二叉树ls的叶子节点, //其中存有k个关键字;b[k]为辅助节点。 //沿从叶子节点到根节点的k条路径将ls调整为失败者树
b0 10 b1 9
b3 6
6 15 … Run3 ∞
b4 12
12 37 … Run4 ∞
10 15 … ∞
Run0
9 18 … ∞
Run1
LoserTree ls[0] ls[1] ls[2] ls[3]
3
1
0
2
b2 20
20 22 … ∞
Run2
ls[4]
4
13
自某叶结点b[s]到败者树 ls[0] 13 根结点ls[0]的调整过程 ls[1] 01
void adjust ( LoserTree &ls, int s ) {//从叶结点b[s]开始,依次将当前的b[s]与父结点指示 的失败者进行比较 //将失败者所在归并段的段号记入父节点中。
t = (s + k) / 2; // ls[t] 是b[s]的父节点
while(t > 0) { if (b[s].key > b[ls[t]].key sÅÆls[t];
y 外部排序需要的总时间为:
tES = m*tIS + d*tIO + s*u*tmg
内部排序所 需总时间
外存读写所 需总时间
内部归并所 需总时间
y 因为tIO >> tmg,想要提高外排序的速度,应减少总读 写次数 d.
为了减小d,应该减小总归并趟数s。 s = ⎡logkm⎤
减小总读写次数 d的途径:增加归并路数k或减小初始段数m。
ls[0] 55 ls[1] 55
ls[2] 55
创建败者树: 插入b4(12)
5 ls[3]
b5 -∞
ls[4] 54
b0 10
b3 6
b4 12
10 15
b1 9
9 18
6 15
12 37
… ∞
… ∞
… Run3 ∞
…
Run0
Run1
∞
b2 20
20 22 … ∞
Run2
17
败者树-创建
3)依次从b[k-1],b[k2],…,b[0]出发调整失败者;
y 如果减小m,则需要增加内存的使用量。 y 但是内存的限制是一定的,如何在不增加内存的情况
下减少m呢?
减小总读写磁盘次数 d的途径2:减小初始段数m.
25
10.3 置换选择排序
y Replacement-Selection Sorting y 在整个排序的过程中,选择最小关键字和输入输出交
叉或平行进行。 y 设输入文件FI中24个对象的关键码序列为 {51,49, 39,
20 22 … ∞
Run2
18
败者树-创建
3)依次从b[k-1],b[k2],…,b[0]出发调整失败者; 方法:将bi与其父节点指示的 上次比较的失败者相比.
ls[2] 3
ls[0] 55 ls[1] 55
创建败者树: 插入b2(20)
52 ls[3]
b5
ls[4] 4
b0 10 b1 9
10
9
b[k].key = MINKEY; //设MINKEY为可能的最小值 for ( i =0; i < k; ++i ) ls[i] = k;//所有中间节点初始化为k for ( i =k-1; i >=0; --i ) Adjust( ls, i);
}// CreateLoserTree
22
void k-Merge( LoserTree &ls, External &b ) {//利用败者树将编号从0到k-1的k个输入归并到输出 段。 //bfo[0r(]到i =b0[;ki -<1]k记; +录+ik)个in输pu入t(段b中[i].当ke前y )记; /录/输的入关键字
CreateLoserTree(ls); //创建初始败者树 while ( b[ls[0]].key ! = MAXKEY{
q = ls[0]; // q指示当前最小关键字所在段号 output(q); //输出q段中当前记录 input(b[q].key, q); //输入q段中下一个记录 Adjust(ls, q); //调整败者树 } //while
23
}// k-Merge
k-merge 算法
y 归并路数 k 的选择不是越大越好。归并路数 k增大 时,相应需增加输入缓冲区个数。如果可供使用的内 存空间不变,势必要减少每个输入缓冲区的容量,使 内外存交换数据的次数增大。
24
10.3 置换选择排序
y 归并的趟数:s = ⎡logkm⎤ y m=记录总数n / 内存可容纳的记录数目;
R1 R2 R3 R4 R5 R6 R7 R8 R9 R10
R12
R34
R56
R78
R910
R1234
R5678
R12345678
R
5
y 假设有n=10000个记录,分为m=10个段,每段有l=1000 个记录。
y 进行k=2路归并,则需要进行s=4趟归并。
y 总归并趟数s = ⎡logkm⎤ (归并树的高度 )
t = t/2; } ls[0] = s;
败者树的高度为 ⎡log2k⎤,在每次调整,找 下一 个具有最小关键码对象时, 最多做 ⎡log2k⎤ 次关键码比较。
}// adjust 15
败者树-创建
1)设b[k]为可能的最小值; 2)设初值:ls[i]=k;
初始化败者树
ls[0] 5 ls[1] 5
冠军 (最小对象), 输出段1当前对象
ls[2] 04
2 ls[3]
ls[4] 43
b0 10 b1 9
b2 20
b3 165
15 … Run3 ∞
b4 12
12 37 … Run4 ∞
10 15 … ∞
Run0
9 18 … ∞
Run1
20 22 … ∞
Run2
14
败者树-调整
//自某叶结点b[s]到败者树根结点ls[0]的调整算法
内部排序所 需总时间
外存读写所 需总时间
内部归并所 需总时间
7
10.2 多路平衡归并排序 k-way Balanced merging
y 对于k 路平衡归并,如果有 m 个初始归并段,需要归
并⎡logkm⎤ 趟。
s = ⎡logkm⎤
6路平衡归并树:36个初始归并段
减小总读写磁盘次数 d的途径1:增加归并路数k
y 再假设磁盘每个物理块可容纳200个记录,则每趟需要50 次读50次写操作。全部排序共需要d=500次读写。
y 外部排序需要的总时间为:
tES = m*tIS + d*tIO + s*u*tmg
内部排序所 需总时间
外存读写所 需总时间
内部归并所 需总时间
则上例中tES = 10*tIS + 500*tIO + 4*u*tmg 6
y 在排序过程中必须不断地在内存与外存之间传送数据。 y 外部存储设备:磁带、磁盘
3
外部排序方法
y 基于磁盘进行的排序多使用归并排序方法。
y 排序过程主要分为两个阶段: y (1)建立用于外排序的内存缓冲区。根据它们 的大小将输入文件划分为若干段,用某种内排 序方法对各段进行排序。这些经过排序的段叫 做初始归并段或初始顺串 (Run)。当它们生成 后就被写到外存中去。
ls[2] 5
5 ls[3]
ls[4]
b5
5
b0 10 b1 9
10
9
-∞ b3 6
b4 12
15
18
6 15
12 37
… ∞
… ∞
… Run3 ∞
…
Run0
Run1
∞
b2 20
20 22 … ∞
Run2
16
败者树-创建
3)依次从b[k-1],b[k2],…,b[0]出发调整失败者;
方法:将bi与其父节点指示的 上次比较的失败者相比.
(k −1) log2 k
log
2
m(n
−
1)
log2 log2
k k
log2
m(n
−1)
=
log2
m(n
−1)
利用败者树,只要内存空间允许, 增大归并路
数 k, 将有效地减少归并树深度, 从而减少读
写磁盘次数 d, 提高外排序的速度。
减小总读写磁盘次数 d的途径1:增加归并路数k
10
Reivew
12
Run0: {10, 15,…, ∞} Run1: {9, 18,…, ∞} Run2: {20, 22,…, ∞} Run3: {6, 15,…, ∞} Run4: {12, 37,…, ∞}
ls[0] 3 ls[1] 1
冠军 (最小对象), 输出段3当前对象
0 ls[2]
2 ls[3]
选中 ls[4] 4
y (2)仿照内排序中所介绍过的归并树模式, 把第一阶段生成的初始归并段加以归并,一趟 趟地扩大归并段和减少归并段个数,直到最后 归并成一个大归并段(有序文件)为止。
4
y 假设有n=10000个记录,分为m=10个段,每段有l=1000 个记录。
y 进行k=2路归并,则需要进行s=4趟归并。
y 总归并趟数s等于归并树的高度 ⎡log2m⎤
-∞ b3 6
b4 12
15
18
6 15
12 37
… ∞
… ∞
… Run3 ∞
…
Run0
Run1
∞
b2 20
20 22 … ∞
Run2
19
败者树-创建
3)依次从b[k-1],b[k2],…,b[0]出发调整失败者; 方法:将bi与其父节点指示的 上次比较的失败者相比.
ls[2] 3
ls[0] 55 ls[1] 51
1
内容
y 10.1 外部排序的概念和方法 y 10.2 多路平衡归并排序 y 10.3 置换-选择排序 y 10.4 最佳归并树
2
10.1 外部排序的概念和方法
y 外排序:基于外部存储设备(或文件)的排序技术就是外 排序。
y 当待排序的对象数目特别多时,在内存中不能一次处理。 必须把它们以文件的形式存放于外存,排序时再把它们一 部分一部分调入内存进行处理。