第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); }