1 / 36

10.4 归并排序 基本思想 把 k(k ≥2)个有序子文件合并在一起,形成一个新的有序 文件。 同时归并 k 个有序子文件的排序过程称为 k- 路归并排序。

10.4 归并排序 基本思想 把 k(k ≥2)个有序子文件合并在一起,形成一个新的有序 文件。 同时归并 k 个有序子文件的排序过程称为 k- 路归并排序。 2-路归并排序 ---归并2个有序子文件的排序。 例. 将有序文件 A 和 B 归并为有序文件 C。 A =(2,10,15,18,21,30) B =(5,20,35,40) 按从小至大的次序从 A 或 B 中依次取出2,5,10,15,...,40, 顺序归并到 C 中,得: C =(2,5,10,15,18,20,21,30,35,40).

garth
Download Presentation

10.4 归并排序 基本思想 把 k(k ≥2)个有序子文件合并在一起,形成一个新的有序 文件。 同时归并 k 个有序子文件的排序过程称为 k- 路归并排序。

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 10.4 归并排序 基本思想 把k(k≥2)个有序子文件合并在一起,形成一个新的有序 文件。 同时归并k个有序子文件的排序过程称为k-路归并排序。 2-路归并排序---归并2个有序子文件的排序。 例. 将有序文件A和B归并为有序文件C。 A=(2,10,15,18,21,30) B=(5,20,35,40) 按从小至大的次序从A或B中依次取出2,5,10,15,...,40, 顺序归并到C中,得: C=(2,5,10,15,18,20,21,30,35,40)

  2. 一般地,2-路归并过程为: 假定文件r[low..high]中的相邻子文件(子表) (r[low],r[low+1],...,r[mid])和(r[mid+1],...,r[high]) 为有序子文件,其中:low≤mid<high 。 将这两个相邻有序子文件归并为有序文件y[low..high],即: (y[low],y[low+1],...,y[high]) r[9..16] 例 y[9..16] 9 10 11 12 13 14 15 16 9 10 11 12 13 14 15 16 i→ k→ 有序 子表 2-路归并 有序文件(表) j→ 有序 子表

  3.  将两个有序子文件归并为有一个有序文件的算法 将两个有序子文件归并为有一个有序文件的算法 void merge(r,y,low,mid,high) RecType r[],y[];int low,mid,high; { int k=i=low,j=mid+1; while (i<=mid && j<=high) { if (r[i].key<=r[j].key) { y[k]=r[i];i++;}//归并前一个子文件的记录 else    { y[k]=r[j];j++;} //归并后一个子文件的记录    k++;} while (j<=high) //归并后一个子文件余下的记录 { y[k]=r[j]; j++; k++;} while (i<=mid)  //归并前一个子文件余下的记录 { y[k]=r[i]; i++; k++;} } // merge

  4. 2-路归并排序 假定文件(r[1],r[2],...,r[n])中记录是随机排列的,进行 2-路归并排序,首先把它划分为长度均为1的n个有序子文件, 然后对它们逐步进行2-路归并排序。其步骤如下: 第1趟:从r[1..n]中的第1个和第2个有序子文件开始,调用 算法merge,每次归并两个相邻子文件,归并结果放到y[1..n]中。 在y中形成 n/2 个长度为2的有序子文件。若n为奇数,则y中最 后一个子文件的长度为1。 第2趟:把y[1..n]看作输入文件,将 n/2 个有序子文件两 两归并,归并结果回送到r[1..n]中,在r中形成 n/2/2个长度为4的有序子文件。若y中有奇数个子文件,则r中最后一个子文 件的长度为2。 ...... 共计经过 log2n 趟归并,最后得到n个记录的有序文件。

  5. 例1. 对8个记录作2路归并排序,共进行log28=3趟归并。 y[1..8] y[1..8] r[1..8] r[1..8] 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 第1趟 第2趟 第3趟

  6. y[1..8] r[1..8] y[1..8] r[1..8] r[1..8] 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 1 2 3 4 5 6 7 8 9 10 11 例2. 对11个记录作2-路归并排序,进行log211=4趟归并。 第2趟 第1趟 第3趟 第4趟

  7. 一趟归并排序算法: void mergepass(r,y,s) // s为子文件的长度 RecType r[],y[];int s;//将r中的子文件归并到y中 { int i=1; while(i+2*s-1<=n) //两两归并长度均为s的子文件 { merge(r,y,i,i+s-1,i+2*s-1); i=i+2*s; } if (i+s-1<n) //最后两个子长度为s和长度不足s的文件 merge(r,y,i,i+s-1,n); else while(i<=n) //复制最后一个子文件,长度≤s { y[i]=r[i]; i++; } }

  8. 调用算法mergepass,对文件r[1..n]归并排序的算法调用算法mergepass,对文件r[1..n]归并排序的算法 void mergesort(RecType r[],int n) { RecType y[n+1]; int s=1; //子文件初始长度为1 while (s<n) { mergepass(r,y,s); //将r[1..n]归并到y[1..n] s=2*s; //修改子文件长度 mergepass(y,r,s); //将y[1..n]归并到r[1..n] s=2*s; //修改子文件长度 } } 算法分析 ● 对n个记录的文件进行归并排序,共需 log2n趟,每趟 所需比较关键字的次数不超过n, 共比较O(nlog2n)次。 ●每趟移动n个记录, 共移动O(nlog2n)个记录。 ● 归并排序需要一个大小为n的辅助空间y[1..n]。 ● 归并排序是稳定的。

  9. 10.5 交换排序 10.5.1 冒泡排序 基本思想: 设待排序的文件为r[1..n] 第1趟(遍):从r[1]开始,依次比较两个相邻记录的关键字 r[i].key和r[i+1].key,若r[i].key>r[i+1].key,则交换记录 r[i]和r[i+1]的位置;否则,不交换。 (i=1,2,...n-1) 第1趟之后,n个关键字中最大的记录移到了r[n]的位置上。 第2趟:从r[1]开始,依次比较两个相邻记录的关键字 r[i].key和r[i+1].key,若r[i].key>r[i+1].key,则交换记录 r[i]和r[i+1]的位置;否则,不交换。 (i=1,2,...n-2) 第2趟之后,前n-1个关键字中最大的记录移到了r[n-1]的位 置上。 ...... 作完n-1趟,或者不需再交换记录时为止。

  10. 例: 第1趟 第2趟 第3趟 第4趟 ┌─────────┐ ┌─┐ ┌─┐ ┌─┐ k1= 43 21 21 21 21 21 21 21 21 15 15 15 k2= 21 43 43 43 43 43 43 15 15 21 21 21 k3= 89 89 89 15 15 15 15 28 28 28 28 28 k4= 15 15 15 89 28 28 28 43 43 4343 43 k5= 28 28 28 28 89 43 43 4343 43 43 43 k6= 43 43 43 43 43 8989 8989 89 89 89 比较次数=5+4+3+2=14 交换记录的次数=3+2+1=6,移动记录次数=3*6=18

  11. 算法分析 ● 最好情况: 待排序的文件已是有序文件,只需要进行1趟 排序,共计比较关键字的次数为 n-1 不交换记录。 ●最坏情况: 要经过n-1趟排序,所需总的比较关键字的次 数为 (n-1)+(n-2)+...+1=n(n-1)/2 交换记录的次数最多为 (n-1)+(n-2)+...+1=n(n-1)/2 移动记录次数最多为 3n(n-1)/2 。 ●只需要少量中间变量作为辅助空间。 ● 算法是稳定的。

  12. 冒泡排序算法 // 对n个整数按递增次序作冒泡排序 Void bubble1(int a[],int n) { int i,j,temp; for(i=0;i<n-1;i++) //作n-1趟排序 for(j=0;j<n-1-i;j++) if (a[j]>a[j+1]) { temp=a[j]; //交换记录 a[j]=a[j+1]; a[j+1]=temp; } for(i=0;i<n;i++) printf("%d",a[i]); //输出排序后的元素 }

  13. 改进的冒泡排序算法 void bubblesort(RecType r[],int n) { int i,j,swap; RecType temp; j=1; //置比较的趟数为1 do{ swap=0; //置交换标志为0 for (i=1;i<=n-j;i++) { if (r[i].key>r[i+1].key) { temp=r[i]; //交换记录` r[i]=r[i+1]; r[i+1]=temp; swap=1; //置交换标志为1 } j++; //作下一趟排序 } } while (j<n && swap); } //未作完n-1趟,且标志为1

  14. 10.5.2 快速排序 基本思想:首先在r[1..n]中,确定一个r[i],经过比较和 移动,将r[i]放到"中间"某个位置上,使得r[i]左边所有记录 的关键字小于等于r[i].key,r[i]右边所有记录的关键字大于等 于r[i].key。以r[i]为界,将文件划分为左、右两个子文件。 用同样的方法分别对这两个子文件进行划分, 得到4个更小 的子文件。继续进行下去,使得每个子文件只有一个记录为止, 便得到原文件的有序文件。 例. 给定文件(20,05,37,08,63,12,59,15,44,08),选 用第1个元素20进行划分: x ↑ ↑ i j

  15. x ↑ ↑ i j x 例 ↑ ↑ ↑ ↑ j i x ↑ ↑ ↑ ↑ i j x ↑ ↑ i j

  16. x ↑ ↑ ↑ ↑ i j 例 x ↑ ↑ ↑ ↑ i j x ↑ ↑ i j x ↑ ↑ 左子文件 右子文件 i j

  17. 快速排序 void quksort(r,low,high) RecType r[];int low,high; { RecType x;int i,j; if (low<high) //有两个以上记录 { i=low;j=high;x=r[i]; //保存记录到变量x中 do{ while (i<j && r[j].key>x.key) j--; //j向左端扫描 if (i<j) //i,j未相遇 { r[i]=r[j]; i++; while(i<j && r[i].key<x.key) i++; //i向右端扫描 if (i<j) { r[j]=r[i];j--; } } } while (i!=j); //i,j未相遇

  18. r[i]=x; //划分结束 quksort(r,low,i-1); //递归处理左子文件 quksort(r,i+1,high); //递归处理右子文件 } } 对文件r[1..n]快速排序 void quicksort(RecType r[],int n) { quksort(r,1,n); }

  19. 算法分析 ● 就平均速度而言,快速排序是已知内部排序方法中最好 的一种排序方法,其时间复杂度为O(nlog(n))。 ●但是,在最坏情况下,快速排序所需的比较次数和冒泡 排序的比较次数相同,其时间复杂度为O(n2)。 ●快速排序需要一个栈作辅助空间,用来实现递归处理左、 右子文件。在最坏情况下,递归深度为n,因此所需栈的空间大 小为O(n)数量级。 ● 快速排序是不稳定的。

  20. 10.6 基数排序 基数排序的一般过程: 将任一关键字K看做一个d元组:K=(K1,K2, … ,Kd) 其中:K1是最高位, Kd是最低位,按组成关键字的每一位的值进行排序。 排序过程(以十进制正整数为例): 设文件有n个记录(R1,R2, … ,Rn),记录Ri的关键字为Ki,且Ki是不超d位的非负整数。 ⅰ:建立十个队列,编号分别为0,1,2, … ,9 ⅱ:重复执行d遍 ⒈分配:若Ki【d】=j,则Ri放入第j号队列(j=0,1,…,9) ⒉收集:按0,1, … ,9的次序将各队列中的记录收集和排列起来。 ⒊ d=d-1

  21. 队列 例:设有关键字序列: 分配

  22. 队列 队列 收集 分配

  23. 队列 收 集 问题:各队列的长度 如何确定?

  24. A[ ] 例2:已知 n=12, d=2,r=10(12个2位的十进制整数,低位优先) COUNT [0..9] 各队列中的记录个数 分配 收集 B[ ]

  25. 0 1 2 3 4 5 6 7 8 9 10 11 a[ ] 分配 count[0..9] 收集 b[ ] 0 1 2 3 4 5 6 7 8 9 10 11

  26. 参考程序如下: for (i=0,d=1;i<k; i++,d*=r) { for ( j=0; j<r; j++) count[j]=0; //初始化 for( j=0; j<n; j++) count[ a[j] / d % r]++; //统计各队列中的记录个数 for ( j=1; j<r; j++) count[j]=count[j-1]+count[j]; //分配 for( j=n-1; j>=0; j--) b[--count[a[j] / d % r]]=a[j]; //收集 for( j=0; j<n; j++) a[j]=b[j]; } 注: d — 1为个位,10为十位,… k — 整数的最大位数; r — 数值的基数(如八进制数,十进制数等); n —关键字个数。

  27. 内排序小结 内排序方法 ●插入排序:直接插入排序; ●选择排序:简单选择/选择排序;堆排序 ●归并排序 2-路归并排序 k-路归并排序 ● 交换排序 冒泡排序: 快速排序 ● 基数排序 最低位优先法

  28. .什么是排序,什么是排序的稳定性 排序算法分析 (1)时间复杂度 ● 对n个记录排序,所需比较关键字的次数; 最好情况;最坏情况;平均情况 ● 对n个记录排序,所需移动记录的次数; 最好情况;最坏情况;平均情况 (2)空间复杂度 排序过程中,除文件中的记录所占的空间外, 所需的辅助存储空间的大小。

  29. 第11章 外部排序 • 外部排序思想 设磁盘有4500个记录的文件,磁盘的读∕写单位是250个记录的数据块,内存只能提供1500个记录的空间,试对此文件进行排序。 解:ⅰ⑴ 从磁盘文件输入三个数据块,计750个记录至内存,排序后再写入磁盘; ⑵ 按⑴同样的方法得如下归并段: R1 R2 R3 R4 R5 R6 1~750 751~1500 1501 ~2250 2251 ~3000 3001 ~ 3750 3751 ~ 4500

  30. R1 R2 磁盘(1500个记录) 部分有序 ⅱ ⑴归并R1,R2 BF1 BF2 内存区 3块计750个记录 BF3 磁盘(1500个记录) 整体有序 R12

  31. R1 R2 R3 R4 R5 R6 ⅱ ⑵ 按⑴同样得方法得:R34,R56,R1234,R123456 R12 R34 R56 1遍 1500 1500 1500 2/3遍 R1234 3000 1遍 R123456 4500个记录整体有序

  32. 建立初始归并段要把全部记录(4500)读写一遍,完成全部记录的归并要把(4500)个记录读写8/3遍。下面分析三路归并的情况:建立初始归并段要把全部记录(4500)读写一遍,完成全部记录的归并要把(4500)个记录读写8/3遍。下面分析三路归并的情况: R1 R2 R3 R4 R5 R6 完成全部记录的归并要把(4500)个记录读写2遍。

  33. 结论:M个初始归并段进行K路归并时,归并的趟数为:S = log(K(M)) 证明:设M为K的整幂次,即M=K**s (s为整数) 第一趟归并得 k**(s-1)个为归并段; 第二趟归并得 k**(s-2)个为归并段; … 第s趟归并得 k**s-s=1个归并段; 由m=k**s ,有log(k(m))=s 当m不是k的整幂次时,归并趟数是 log(k(m))的上界函数log(K(M))证毕 上式说明:要s ,只有K 或m

  34. 权值愈小的结点,离根愈近? • 最佳归并树 设有8个初始归并段,其长度(以外存物理块为单位)分别为2,3,6,9,12,17,18,24,求其3路最佳归并树,并求其WPL和对外存的访问次数。 解1: 2 3 6 9 11 12 17 18 24 32 59 0 91 WPL=(2+3+6)*3+(9+12) *2+(17+18+24)*2 =193 读写次数=WPL*2 =386

  35. 0 2 3 权值愈小的结点,离根愈远! • 解2:∵ k-(m-1) mod (k-1)-1 =3-(8-1) mod (3-1)-1=1 ∴ 虚设一个长度为“0”的归并段。 5 6 9 12 17 18 20 24 47 91 WPL=(0+2+3)*3+(6+9+12+17+18)*2+24*1=163 读写次数=WPL*2=326

  36. 如何判定虚设段的个数? 令 M ——初始归并段的个数 K ——归并的路数 若(M-1) MOD (K-1)= 0,则不加虚设段; 否则需加 P个虚设段。 P = K-(M-1) MOD (K-1) -1

More Related