第11章 外排序
本章主要介绍下列内容(教材第三章)
1. 排列问题(归入第10章中的回溯法一并介绍,此处略)
2. 组合问题(同上处理)
3. 外排序及广义斐波那契(FIBONACCI)数
4. 传递闭包及Warshall算法(学生参考《数据结构》部分的Floyd算法自学)
课时分配:第3节讲授三个学时、上机三个学时
重点、难点:多遍归并外排序初始有序段的分配、虚段的添加算法
第三节 外排序及广义斐波那契(FIBONACCI)数
本节主要按教材内容介绍,但在适当位置要补充以下内容:
1.归并两个有序文件的细节讨论(见补充材料)
2.为形成尽可能长的初始归并段:
(1)用具有一定技巧的内部排序方法(见补充材料)
(2)置换选择排序(见《数据结构》外部排序一章)
3.在多遍归并(中途不分配)外排序中确定初始分布补充方法:
K阶斐波那契序列定义为:
??
??
?
≥+++
?=
?<≤
=
?+?? Kjfff
Kj
Kj
f
K
j
K
Kj
K
Kj
K
j
)(
1
)(
1
)(
)( 11
100
L
K阶广义斐波那契序列定义为:
??
???
≥+++
?≤≤=
?+?? KjFFF
KjF
K
j
K
kj
K
Kj
K
j )(
1
)(
1
)(
)( 101
L
已知有序段总数x,确定K条带分布的步骤为:
第一步:找出使 xF Kj ≥)( 的最小j值;
第二步:计算各带有序段数(等号右边为另法):
)()(
1
)(
1
)(
2
)(
1
)(
2
)()()(
2
)(
1
)(
1
K
Kj
KK
Kj
K
j
K
j
K
K
j
K
Kj
K
j
K
j
K
ftffft
fffft
?+???
???
?+++=
+++=
L
L 或
)(
1
)(
)(
2
)(
1
)(
1
K
j
K
K
K
j
K
j
K
K
ft
fft
?
???
=
+=
M
)(
2
)(
1
)(
)(
3
)(
2
)(
1
)(
)2(
)(
3
)(
4
)(
)1(
)(
2
)(
3
K
j
K
K
K
K
K
j
K
K
K
K
K
Kj
KK
K
Kj
KK
ftt
ftt
ftt
ftt
??
???
??
??
?=
?=
?=
?=
M
例:给定有序段总数 253 及六根磁带进行五路多遍归并,试为五根输入带确定有序段
分配数目。
① 确定j
)5(
jF :
9774972531296533179511111
11109876543210
62312061 31 16 8 4 2 1 1 0 0 0 0:f )5(j
j-1项
② 计算分布:
164731478551631
5545981631
59261481631
612481631
)5(
1
)5(
5
)5(
2
)5(
1
)5(
4
)5(
3
)5(
2
)5(
1
)5(
3
)5(
4
)5(
3
)5(
2
)5(
1
)5(
2
)5(
5
)5(
4
)5(
3
)5(
2
)5(
1
)5(
1
?====?=+=+=
=?=++=++=
=?=+++=+++=
=++++=++++=
???
???
????
?????
jjj
jjj
jjjj
jjjjj
ftfft
ffft
fffft
ffffft
4.P82 有序段初始分配演示(算法过程)
举50个有序段的分配为例(但事先并不知道是50个有序段)
j项≥253
A B C D E F
1 2 3 4 5 6
若实际为40个段,D的最后状态如何?
共25个,这时各带上的实际段数为:
P83 归并算法执行过程演示
初始段为40个时,按教材P82分配算法得:
1 2 3 4 5 6
实=A-D: 11 10 9 6 刚好 40个实 5=l
虚=D: 5 5 5 6 8个段! 25个虚
1 1 1 1 1 0 实 A:
1 1 1 1 1 0 虚 D:
2 2 2 2 1 0 L:=2;B=1: A 后算
1 1 1 1 0 0 D 先算
0 0 0 0 0 0 D的变化过程
0 0 0 0 0 0 J:=1; l =1:
A
D
4 4 4 3 2 0 L:=3;B=2: A 共17
2 2 2 1 1 0 D
8 8 7 6 4 0 L:=4;B=4: A 共33
0 0 0 0 0 0 D的变化过程
两遍对应 A 值差即
为相应D值初值。
例如:
111100
111110:)
222210:1
l
l
?
+
对同一个j值
4 4 3 3 2 0 D
0 0 0 0 0 0 D变化过程
16 15 14 12 8 0 L:=5;B=8: A 共65
8 7 7 6 4 0 D共65-33=32
7
6
5
4
3
7
6
5
4
3
7
6
5
4
3
6
6
5
4
3
4
4
4
4
3
0
0
0
0
0
D变化,最后使D中剩下15作为虚段(记
录)。这时,各带上的实际段数为
13 12 11 9 5,共50个。
5 5 5 6 4 0 D
11 10 9 6 4 0 A
实际为50
余量为15
4
4
Tape 1 2 3 4 5 6
L=5时:且每个带上都有记录,故:
归:D: 4 4 4 5 3 1 原A-D:未变
3 3 3 4 2 2
2 2 2 3 1 3
1 1 1 2 0 4 11 10 9 6 4 0
因A[5]即A[T-1]还不为0,故仍repeat:
0 0 0 1 * 4 11 10 9 6 3 1
* * * 0 * 4 10 9 8 6 2 2
* * * * * 4 9 8 7 5 1 3
* * * * * 4 8 7 6 4 0 4
L=4时,先调整:
1 2 3 4 5 6
A 4 8 7 6 4 0
D 4 0 0 0 0 0
Tape原: 6 1 2 3 4 5
归: D 4 0 0 0 0 0 A 4 8 7 6 4 0
3 * * * * * 4 7 6 5 3 1
2 * * * * * 4 6 5 4 2 2
1 * * * * * 4 5 4 3 1 3
0 * * * * * 4 4 3 2 0 4
L=3时,先调整
逻辑Tape 1 2 3 4 5 6
物理Tape 5 6 1 2 3 4
归:D 0 0 0 0 0 0 A 4 4 4 3 2 0
* * * * * * 3 3 3 2 1 1
* * * * * * 2 2 2 1 0 2
L=2时,先调整
逻辑Tape 1 2 3 4 5 6
物理Tape 4 5 6 1 2 3
归:D 0 0 0 0 0 0 A 2 2 2 2 1 0
* * * * * * 1 1 1 1 0 1
L=1时,先调整,逻:1 2 3 4 5 6 物:3 4 5 6 1 2
归:D 0 0 0 0 0 0 A 1 1 1 1 1 0
* * * * * * 0 0 0 0 0 1
共8个段
虚实均空
两者同时为空
Repeat条件满足,停止循环,进入下遍。
退出repeat循环
退出repeat循环
L=0时,先调整:
逻Tape 1 2 3 4 5 6
物Tape 2 3 4 5 6 1
归:D全0 A 1 0 0 0 0 0
此时,因L=0,while循环结束,全部记录都在2号物理磁带上,只有一个有序段,可
作排序结果输出。
第四节 传递闭包及Warshall算法
一、先对上节内容再作小结以引入第三节课的讨论。
对教材上所给出的多遍归并外排序算法,形成初始归并段有两种方法:
1.一个原始记录作为一个有序段;
2.根据内存大小(假设可容纳n个记录),将原始记录n个为一组读入内存排序以形
成初始有序段(最后一个有序段可能不足n个记录), 然 后 再 按新 构 成 的有序段总数进行分
配,必要时添加虚记录(一个为一段)以使有序段总数等于最接近的FeiboNacci数。
二、如何利用《数据结构》已有知识求关系的传递闭色(见教材P84-P88)。
三、课堂讨论及内部排序竞赛评奖。(参考程序见第8章内部排序的补充)
/*一个简单的外排程序*/
#include<stdio.h>
#include<stdlib.h>
#include<alloc.h>
#define N 1000
int huge *a;
main()
{ long int n,i,j,k;int min;FILE *f;char s[20];
printf("source file name=");scanf("%s",s);
n=0;f=fopen(s,"r");
a=farmalloc(sizeof(int)*(N+1));
while (!feof(f)) {n++;fscanf(f,"%d",&min);a[n]=min;}
for (i=1;i<=n;i++) printf("%8d",a[i]);
printf("\n");
for (i=1;i<=n-1;i++)
{k=i;
for (j=i+1;j<=n;j++) if (a[j]<a[k]) k=j;
if (k!=i) {min=a[k];a[k]=a[i];a[i]=min;}
}
for (i=1;i<=n;i++) printf("%8d",a[i]);
printf("\n");
fclose(f);
printf("target file name=");scanf("%s",s);
f=fopen(s,"w");
for (i=1;i<=n;i++) fprintf(f,"%8d",a[i]);
fclose(f);
farfree(a);}
/*一个简单的外部归并程序*/
#include<stdio.h>
#include<stdlib.h>
#include<alloc.h>
#define MAX 32767
int huge *a;
main()
{
int ar,br,cr;FILE *a,*b,*c;char s[20];
printf("source file name 1 :");scanf("%s",s);a=fopen(s,"r");
printf("source file name 2 :");scanf("%s",s);b=fopen(s,"r");
printf("target file name :");scanf("%s",s);c=fopen(s,"w");
if (feof(a)) ar=MAX;else fscanf(a,"%d",&ar);
if (feof(b)) br=MAX;else fscanf(b,"%d",&br);
while (!feof(a) || !feof(b))
{ if (ar<=br)
{ fprintf(c,"%8d",ar);if (feof(a)) ar=MAX;else fscanf(a,"%d",&ar);}
else { fprintf(c,"%8d",br);if (feof(b)) br=MAX;else fscanf(b,"%d",&br);}
}
if (ar==MAX && br<MAX) fprintf(c,"%8d",br);
if (br==MAX && ar<MAX) fprintf(c,"%8d",ar);
if (ar<=br && br<MAX) fprintf(c,"%8d%8d",ar,br);
if (ar>br && ar<MAX) fprintf(c,"%8d%8d",br,ar);
fclose(a);fclose(b);fclose(c);
}