88
基基基基 基基基基 基基基基 基基基基 基基基基 基基基基 基基基基基基 基基基 第 9 第 第第

基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

Embed Size (px)

DESCRIPTION

第 9 章 排序. 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序. 9.1 排序的基本概念. 关键字 假设被排序的对象是由一组记录组成的文件,记录由若干个数据项 ( 或域 ) 组成,其中有一项可用来标识一个记录,称为 关键字项 。该数据项的值称为 关键字 (Key) 。 在不易产生混淆时,本章中将关键字项简称为关键字。 排序 所谓排序( Sort ),就是要整理文件中的记录,使它们按关键字递增 ( 或递减 ) 次序重新排列。 - PowerPoint PPT Presentation

Citation preview

Page 1: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

第 9 章 排序

Page 2: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

关键字 关键字 假设被排序的对象是由一组记录组成的假设被排序的对象是由一组记录组成的文件,记录由若干个数据项文件,记录由若干个数据项 (( 或域或域 )) 组成,其中组成,其中有一项可用来标识一个记录,称为有一项可用来标识一个记录,称为关键字项关键字项。该。该数据项的值称为数据项的值称为关键字关键字 (Key)(Key) 。。在不易产生混淆在不易产生混淆时,本章中将关键字项简称为关键字。时,本章中将关键字项简称为关键字。

排序 排序 所谓排序(所谓排序( SortSort ),就是要整理文件中的),就是要整理文件中的记录,使它们按关键字递增记录,使它们按关键字递增 (( 或递减或递减 )) 次序重新次序重新排列。排列。

排序方法的稳定性排序方法的稳定性在待排序的文件中,若存在多在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,则称该关键字的记录之间的相对次序保持不变,则称该排序方法是稳定的排序方法是稳定的;否则,若具有相同关键字的;否则,若具有相同关键字的记录之间的相对次序发生变化,则称该记录之间的相对次序发生变化,则称该排序方法排序方法是不稳定的是不稳定的。。

9.1 9.1 排序的基本概念排序的基本概念

Page 3: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

排序方法的分类排序方法的分类 按是否涉及数据的内、外存交换分类: 按是否涉及数据的内、外存交换分类: 外排序、内排序。 外排序、内排序。 按策略划分内部排序方法: 按策略划分内部排序方法: 插入排序、选择排序、交换排序、归插入排序、选择排序、交换排序、归并排序和基数排序等。并排序和基数排序等。

排序算法性能评价排序算法性能评价 评价排序算法好坏的标准主要有两条:评价排序算法好坏的标准主要有两条:(( 11 )执行算法所需的时间;)执行算法所需的时间; (( 22 )执行算法所需的辅助空间。)执行算法所需的辅助空间。

Page 4: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

不同存储方式的排序过程不同存储方式的排序过程以顺序表作为存储结构:以顺序表作为存储结构: 对记录本身进行物理重排。对记录本身进行物理重排。以链表作为存储结构:以链表作为存储结构: 无须移动记录,仅需修改指针。无须移动记录,仅需修改指针。用顺序的方式存储待排序的记录,但同用顺序的方式存储待排序的记录,但同 时建立一个辅助表。时建立一个辅助表。 只需对辅助表的表目进行物理重排,只需对辅助表的表目进行物理重排,适适 用于难以在链表上实现,仍需避免排用于难以在链表上实现,仍需避免排序序 过程中移动记录的排序方法。 过程中移动记录的排序方法。

Page 5: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

若无特别说明,则所讨论排序均若无特别说明,则所讨论排序均为升序(即按递增排序),并以记录数组为升序(即按递增排序),并以记录数组作为文件的存储结构。同时假定关键字是作为文件的存储结构。同时假定关键字是整数。记录数组的类型说明如下:整数。记录数组的类型说明如下:typedef int KeyTypetypedef int KeyType ; ; typedef structtypedef struct {{ KeyType key KeyType key ;; InfoType otherinfoInfoType otherinfo ;; }RecType}RecType ;;typedef RecType SeqList[n+1]typedef RecType SeqList[n+1] ;;

Page 6: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.2 9.2 插入排序插入排序 基本原理:每步将一个待排序的基本原理:每步将一个待排序的记录,按其关键字大小,插入到前面记录,按其关键字大小,插入到前面已经排好序的一组记录适当位置上,已经排好序的一组记录适当位置上,直到记录全部插入为止。直到记录全部插入为止。

直接插入排序 希尔排序

Page 7: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.2.1 9.2.1 直接插入排序直接插入排序

基本思想:基本思想:假设待排序的记录存假设待排序的记录存放在数组放在数组 R[1..n]R[1..n] 中( 中( R[1..n]R[1..n] 表示数组元素表示数组元素的范围是的范围是从从 R[1]R[1] 到到 R[n]R[n] )。初始时,)。初始时, i=1i=1 ,,R[1]R[1] 自成一个有序区,无序区为自成一个有序区,无序区为 R[2..n]R[2..n] 。然。然后,从后,从 i=2i=2 起直至起直至 i=ni=n ,依次将,依次将 R[i]R[i] 插入当插入当前的有序区前的有序区 R[1..i-1]R[1..i-1] 中,最后,生成含中,最后,生成含 nn 个个记录的有序区。记录的有序区。

Page 8: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

例例 9.1 9.1 直接插入排序举例直接插入排序举例[[ 初始关键字初始关键字 ] [49] 38 65 97 76 13 27 49] [49] 38 65 97 76 13 27 49

j=2 (38) [38 49] 65 97 76j=2 (38) [38 49] 65 97 76 13 27 13 27 4949j=3 (65) [38 49 65] 97 76 13 27 j=3 (65) [38 49 65] 97 76 13 27 4949j=4 (97) [38 49 65 97] 76 13 27 j=4 (97) [38 49 65 97] 76 13 27 4949j=5 (76) [38 49 65 76 97] 13 27 j=5 (76) [38 49 65 76 97] 13 27 4949j=6 (13) [13 38 49 65 76 97] 27 j=6 (13) [13 38 49 65 76 97] 27 4949j=7 (27) [13 27 38 49 65 76 97] j=7 (27) [13 27 38 49 65 76 97] 4949j=8 (j=8 (4949) [13 27 38 49 ) [13 27 38 49 4949 65 76 97] 65 76 97]

监视哨监视哨 R[0]R[0]

图图 9.1 9.1 直接插入排序过程示例直接插入排序过程示例

Page 9: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

直接插入排序算法直接插入排序算法void lnsertSort(SeqList R) { int i,j; for (i=2;i<n;i++) {R[0] = R[i]; j = i-1; while (R[0].key<R[j].key) {R[j+1] = R[j]; j- -; } R[j+1] = R[0]; }}

Page 10: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法中引入附加记录算法中引入附加记录 R[0]R[0] 有两个有两个作用:  ①进入查找循环之前,它保存作用:  ①进入查找循环之前,它保存了了 R[i]R[i] 的副本,使得不至于因记录的后移的副本,使得不至于因记录的后移而丢失而丢失 R[i]R[i] 中的内容;中的内容;

  ②在  ②在 whilewhile 循环“监视”下标变量循环“监视”下标变量 jj是否越界,一旦越界(即是否越界,一旦越界(即 j=0j=0 ),能自动),能自动控制控制 whilewhile 循环的结束,从而避免了在循环的结束,从而避免了在 wwhilehile 循环内的每一次都要检测循环内的每一次都要检测 jj 是否越界是否越界(即省略了循环条件(即省略了循环条件 j>=1j>=1 )。)。

因此,我们把因此,我们把 R[0]R[0] 称为“称为“监视监视哨哨”。”。

Page 11: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

直接插入排序的算法分析直接插入排序的算法分析 直接插入排序算法由两重循环组成,对于有直接插入排序算法由两重循环组成,对于有 nn 个记录个记录

的排序,内循环表明完成一趟排序所需进行的记录关的排序,内循环表明完成一趟排序所需进行的记录关键字间的比较和记录的后移。键字间的比较和记录的后移。

若初始时关键字递增有序,这是最好情况。每一趟排若初始时关键字递增有序,这是最好情况。每一趟排序中仅需进行一次关键字的比较,所以总的比较次数序中仅需进行一次关键字的比较,所以总的比较次数为为 n-1n-1 。在。在 whilewhile 循环之前和之中,至少要移动记录循环之前和之中,至少要移动记录两次,所以总的比较次数为两次,所以总的比较次数为 2(n-1)2(n-1) 。。

若初始时关键字递减有序,这是最坏情况。这时的记若初始时关键字递减有序,这是最坏情况。这时的记录比较和移动次数分别为:录比较和移动次数分别为:

n

i

n

i

nOnni

nOnni

2

2

2

2

)(2/)4)(1()21(

)(2/)1)(2(

移动次数的最大值

比较次数的最大值

直接插入排序是一种稳定的排序方法。直接插入排序是一种稳定的排序方法。

Page 12: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.2.2 9.2.2 希尔排序希尔排序 19591959 年由年由 D.L. ShellD.L. Shell 提出,又称缩小提出,又称缩小增量排序增量排序 (Diminishing-increment so(Diminishing-increment sort) rt) 。。

基本思想:在直接插入排序中,只比较基本思想:在直接插入排序中,只比较相邻的结点,一次比较最多把结点移动相邻的结点,一次比较最多把结点移动一个位置。如果对位置间隔较大距离的一个位置。如果对位置间隔较大距离的结点进行比较,使得结点在比较以后能结点进行比较,使得结点在比较以后能够一次跨过较大的距离,这样就可以提够一次跨过较大的距离,这样就可以提高排序的速度。高排序的速度。

Page 13: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

希尔排序的基本过程希尔排序的基本过程

设待排序的记录序列有设待排序的记录序列有 nn 个个记录,首先取一个整数记录,首先取一个整数 dd11 <n <n 作为间隔,作为间隔,将全部记录分为将全部记录分为 dd11 个子序列,所有距个子序列,所有距离为离为 dd11 的记录放在同一个序列中,在的记录放在同一个序列中,在每一个子序列中分别施行直接插入排序,每一个子序列中分别施行直接插入排序,然后缩小间隔然后缩小间隔 dd22 ,如取,如取 dd22 = = dd11 /2 /2 ,重,重复上述的子序列划分和排序工作,直到复上述的子序列划分和排序工作,直到最后取最后取 ddtt== 1 1 为止。为止。

Page 14: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

希尔排序示例希尔排序示例

[[ 初始关键字初始关键字 ] 49 38 65 97 76 13 27 ] 49 38 65 97 76 13 27 4949 55 04 55 04 (增量为(增量为 55 ))

一趟排序结果: 一趟排序结果: 13 27 13 27 4949 55 04 49 38 65 97 76 55 04 49 38 65 97 76 (增量为(增量为 33 ))

二趟排序结果: 二趟排序结果: 13 04 13 04 4949 38 27 49 55 65 97 76 38 27 49 55 65 97 76(增量为(增量为 11 ))

三趟排序结果: 三趟排序结果: 04 13 27 38 04 13 27 38 4949 49 55 65 76 97 49 55 65 76 97

图图 9.2 9.2 希尔排序过程示例希尔排序过程示例

Page 15: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

希尔排序算法希尔排序算法void ShellPass(SeqList R , int d)

{

for(i=d+1 ; i<=n ; i++)

if(R[i].key<R[i-d].key)

{

R[0]=R[i];j=i-d ;do {

R[j+d]=R[j] ;j=j-d ;}while(j>0&&R[0].key<R[j].key) ;R[j+d]=R[0] ;}

}

void ShellSort(SeqList R)

{

int increment=n ;do {

increment=increment/3+1 ;ShellPass(R , increment) ;}while(increment>1) ;}

Page 16: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

为什么为什么 shellshell 排序的时间性能优于直接插入排排序的时间性能优于直接插入排序呢?序呢?

因为直接插入排序在初态为正序时所需时间最因为直接插入排序在初态为正序时所需时间最少,实际上,初态为基本有序时直接插入排序所需少,实际上,初态为基本有序时直接插入排序所需的比较和移动次数均较少。另一方面,当的比较和移动次数均较少。另一方面,当 nn 值较小值较小时,时, nn 和和 nn22 的差别也较小,即直接插入排序的最好的差别也较小,即直接插入排序的最好时间复杂度时间复杂度 O(n)O(n) 和最坏时间复杂度和最坏时间复杂度 O(nO(n22)) 差别不大。差别不大。在在 shellshell 排序开始时增量较大,分组较多,每组的排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量逐记录数目少,故各组内直接插入较快,后来增量逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但组内元素已经过多次排序,数组已经比较增多,但组内元素已经过多次排序,数组已经比较接近有序状态,所以新的一趟排序过程也较块。接近有序状态,所以新的一趟排序过程也较块。

Page 17: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

希尔排序中增量希尔排序中增量 ddii 的取法的取法

ShellShell 最初的方案是最初的方案是 dd11 = n/2, d = n/2, di+i+

11 = d = dii /2 /2 ,直到,直到 ddtt =1 =1 。。 KnuthKnuth 的方案是的方案是 ddi+1i+1 = d = dii /3+1 /3+1 。。 其它方案有:都取奇数为好;或其它方案有:都取奇数为好;或

ddii 互质为好等等。互质为好等等。

Page 18: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

希尔排序的算法分析希尔排序的算法分析

对希尔排序的复杂度的分析很困难,在特定情况对希尔排序的复杂度的分析很困难,在特定情况下可以准确地估算关键字的比较和记录移动次数,下可以准确地估算关键字的比较和记录移动次数,但是考虑到与增量之间的依赖关系,并要给出完但是考虑到与增量之间的依赖关系,并要给出完整的数学分析,目前还做不到。整的数学分析,目前还做不到。

KnuthKnuth 的统计结论是,平均比较次数和记录平的统计结论是,平均比较次数和记录平均移动次数在均移动次数在 nn1.251.25 与与 1.6n1.6n1.251.25 之间。之间。

希尔排序的稳定性希尔排序的稳定性 希尔排序是一种不稳定的排序方法。见前例,希尔排序是一种不稳定的排序方法。见前例,

未排序前,未排序前, 2525 在在 25* 25* 之前,之前,希尔排序完成后, 希尔排序完成后, 2525 在在 25* 25* 之后,这就说明之后,这就说明希尔排序是不稳定的。希尔排序是不稳定的。

Page 19: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.3 9.3 交换排序交换排序

两种常见的交换排序两种常见的交换排序

冒泡排序 快速排序

基本原理:两两比较待排序的记录的关键基本原理:两两比较待排序的记录的关键字,如果发生逆序,则交换之,直到全部字,如果发生逆序,则交换之,直到全部记录都排好序为止记录都排好序为止。。

Page 20: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.3.1 9.3.1 冒泡排序冒泡排序

将被排序的记录数组将被排序的记录数组 R[1..n]R[1..n] 垂直排列,每个记录垂直排列,每个记录 R[i]R[i] 看作是重量为看作是重量为 RR[i].key[i].key 的气泡。根据轻气泡不能在重气泡之下的原则,从下往上(也可的气泡。根据轻气泡不能在重气泡之下的原则,从下往上(也可以从上往下)扫描数组以从上往下)扫描数组 RR ,凡扫描到违反此原则的轻气泡,就使其向上,凡扫描到违反此原则的轻气泡,就使其向上“飘浮”。如此反复进行,直到最后任何两个气泡都是轻者在上、重者“飘浮”。如此反复进行,直到最后任何两个气泡都是轻者在上、重者在下为止。在下为止。

初始时初始时 R[1..n]R[1..n] 为无序区。第一趟扫描从无序区底部向上依次比较相邻为无序区。第一趟扫描从无序区底部向上依次比较相邻的两个气泡的重量,若发现轻者在下、重者在上,则交换二者的位置。的两个气泡的重量,若发现轻者在下、重者在上,则交换二者的位置。即依次比较即依次比较 (R[n](R[n] ,, R[n-1])R[n-1]) ,, (R[n-1](R[n-1] ,, R[n-2])R[n-2]) ,…,,…, (R[2](R[2] ,, RR[1])[1]) ;对于每对气泡;对于每对气泡 (R[j+1](R[j+1] ,, R[j])R[j]) ,若,若 R[j+1].key<R[j].keyR[j+1].key<R[j].key ,则交,则交换换 R[j+1]R[j+1] 和和 R[j]R[j] 的内容。第一趟扫描完毕时,“最轻”的气泡就飘浮到的内容。第一趟扫描完毕时,“最轻”的气泡就飘浮到该区间的顶部,即关键字最小的记录被放在最高位置该区间的顶部,即关键字最小的记录被放在最高位置 R[1]R[1] 上。第二趟扫上。第二趟扫描扫描描扫描 R[2..n]R[2..n] 。扫描完毕时,“次轻”的气泡飘浮到。扫描完毕时,“次轻”的气泡飘浮到 R[2]R[2] 的位置上。的位置上。最后,经过最后,经过 n-1n-1 趟扫描可得到有序区趟扫描可得到有序区 R[1..n]R[1..n] 。。

冒泡排序的基本思想冒泡排序的基本思想

Page 21: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

例例 9.3 9.3 冒泡排序示例冒泡排序示例49 49 13 13 13 13 13 13 1313 13 13 13 13 13 1338 49 38 49 27 27 27 27 27 2727 27 27 27 27 2765 38 49 65 38 49 38 38 38 38 3838 38 38 38 3897 65 38 49 97 65 38 49 49 49 49 4949 49 49 4976 97 65 76 97 65 4949 4949 4949 4949 494913 76 97 65 65 65 13 76 97 65 65 65 65 6565 6527 27 76 97 76 76 76 27 27 76 97 76 76 76 76764949 4949 4949 76 97 97 97 76 97 97 97 9797

初始 第一趟 第二趟 第三趟 第四趟 第五趟 初始 第一趟 第二趟 第三趟 第四趟 第五趟 第六趟第六趟 最终排序最终排序关键字 排序后 排序后 排序后 排序后 排序后 关键字 排序后 排序后 排序后 排序后 排序后 排序后排序后 ,, 结果结果 已无记录交换,已无记录交换, 冒泡排序终止!冒泡排序终止!

图图 9.3 9.3 冒泡排序过程示例(由下向上比较)冒泡排序过程示例(由下向上比较)

Page 22: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

冒泡排序算法冒泡排序算法void BubbleSort(SeqList R){int i , j , exchange ;for(i=1;i<n;i++) { exchange=0 ; for(j=n-1;j>=i ; j--) if(R[j+1].key<R[j].key) { R[0]=R[j+1] ; R[j+1]=R[j] ; R[j]=R[0] ; exchange=1 ; } if(!exchange) break ; }}

Page 23: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

冒泡排序的算法分析冒泡排序的算法分析 考虑关键字的比较次数和记录移动次数考虑关键字的比较次数和记录移动次数

在最好情况下,初始状态是递增有序的,一趟扫在最好情况下,初始状态是递增有序的,一趟扫描就可完成排序,关键字的比较次数为描就可完成排序,关键字的比较次数为 n-1n-1 ,没,没有记录移动。有记录移动。

若初始状态是反序的,则需要进行若初始状态是反序的,则需要进行 n-1n-1 趟扫描,趟扫描,每趟扫描要进行每趟扫描要进行 n-in-i 次关键字的比较,且每次需次关键字的比较,且每次需要移动记录三次,因此,最大比较次数和移动次要移动记录三次,因此,最大比较次数和移动次数分别为:数分别为:

冒泡排序方法是稳定的。冒泡排序方法是稳定的。

1

1

2

1

1

2

)(2/)1(3)(3

)(2/)1()(

n

i

n

i

nOnnin

nOnnin

移动次数的最大值

比较次数的最大值

Page 24: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

快速排序的基本思想快速排序的基本思想 快速排序方法是一种所需比较次数较少、在快速排序方法是一种所需比较次数较少、在内部排序中速度比较快的排序方法。内部排序中速度比较快的排序方法。

其思想是在待排序的记录序列中任取某个记其思想是在待排序的记录序列中任取某个记录(作为录(作为基准基准)的值作为)的值作为控制值控制值,采用某种,采用某种方法把这个记录放到适当的位置,使得这个方法把这个记录放到适当的位置,使得这个位置的左边的所有记录的值都小于或等于这位置的左边的所有记录的值都小于或等于这个个控制值控制值,而这个位置的右边的所有记录的,而这个位置的右边的所有记录的值都大于或等于这个值都大于或等于这个控制值控制值。。

9.3.2 9.3.2 快速排序快速排序

Page 25: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

例例 9.4 (a) 9.4 (a) 一趟快速排序示例(一次划分过一趟快速排序示例(一次划分过程)程)

49 38 65 97 76 13 27 49’

38 65 97 76 13 27 49’

27 38 65 97 76 13 49’

27 38 97 76 13 65 49’

27 38 13 97 76 65 49’

27 38 13 76 97 65 49’

27 38 13 49

76 49

65 49’

49

pivot

i

i

i

i

i

i

i

j

j

j

j

j

j

j

Page 26: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

例例 9.4 (b) 9.4 (b) 各趟排序后的状态各趟排序后的状态49 38 65 97 76 13 27 49

27 38 13 49 76 97 65 49’

13 27 38 49 49’

65 76 97

13 27 38 49 49’

65 76 97

13 27 38 49 49’

65 76 97

初始关键字

一趟排序之后

二趟排序之后

三趟排序之后

排序结果

图中,粉红色关键字表示基准关键字 pivot.key 。

方括号 表示划分后的区间。

Page 27: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

快速排序算法快速排序算法void QuickSort(SeqList R , int low , int high)

{

int pivotpos ; if(low<high)

{

pivotpos=Partition(R , low , high) ; QuickSort(R , low , pivotpos-1) ; QuickSort(R , pivotpos+1 , high) ; }

}

Page 28: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

划分算法划分算法int Partition(SeqList R , int i , int j){ReceType pivot=R[i] ; while(i<j) {while(i<j&&R[j].key>=pivot.key) j-- ; if(i<j) R[i++]=R[j] ; while(i<j&&R[i].key<=pivot.key) i++ ; if(i<j) R[j--]=R[i]; } //endwhile R[i]=pivot ; return i ;}

Page 29: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

快速排序的算法分析快速排序的算法分析

最好情况是每次所取的基准都是当前无序区的“中最好情况是每次所取的基准都是当前无序区的“中值”记录,划分的结果是基准的左右两个无序子区的值”记录,划分的结果是基准的左右两个无序子区的长度大致相等。长度大致相等。

1

1

2 )(2/)1()(n

i

nOnnin比较次数的最大值

考虑关键字的比较次数和记录移动次数考虑关键字的比较次数和记录移动次数

最坏情况是每次划分选取的基准都是当前无序区最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基中关键字最小(或最大)的记录,划分的结果是基准的左边(或右边)为空,划分前后无序区的元素准的左边(或右边)为空,划分前后无序区的元素个数减少一个,因此,排序必须做个数减少一个,因此,排序必须做 n-1n-1 趟,每一趟趟,每一趟中需做中需做 n-in-i 次比较,所以最大比较次数为次比较,所以最大比较次数为

Page 30: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

快速排序的记录移动次数不会大于比较次数,所以,快速排序的最坏时间复杂度为 O(n2) ;最好时间复杂度为 O(nlog2n) 。 快速排序的平均时间复杂度也是 (nlog2n) ,空间复杂度为 O(log2n) 。 快速排序是不稳定的排序方法。例如:初始关键字 2 2* 1 一趟排序后 1 2* 2二趟排序后 1 2* 2 排序结果 1 2* 2

Page 31: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.4 9.4 选择排序选择排序

两种常见的选择排序两种常见的选择排序

直接选择排序 堆排序

基本思想基本思想 :: 将待排序的记录分为已排序将待排序的记录分为已排序(( 初始为空初始为空 )) 和未排序两组,依次将未排序和未排序两组,依次将未排序的记录中关键字值最小的记录放入已排序的记录中关键字值最小的记录放入已排序的记录组的最后,直到记录直到排序完毕的记录组的最后,直到记录直到排序完毕为止。为止。

Page 32: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

直接选择排序的基本思想直接选择排序的基本思想

(1)(1) 在一组记录在一组记录 R[i]R[i] 到到 R[n]R[n] 中选择具中选择具有最小关键字的记录。有最小关键字的记录。

(2)(2) 若它不是这组记录中的第一个记录,若它不是这组记录中的第一个记录,则将它与这组记录中的第一个记录对调。则将它与这组记录中的第一个记录对调。

(3) (3) 除去这个最小关键字的记录,在剩除去这个最小关键字的记录,在剩下的记录中重复下的记录中重复 (1)(1) 、、 (2)(2) 步,直到剩步,直到剩余记录只有一个为止。余记录只有一个为止。

9.4.1 直接选择排序直接选择排序

Page 33: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

49 38 65 97 76 13 27 49’

13 38 65 97 76 49 27 49’

13 27 65 97 76 49 38 49’

13 27 38 97 76 49 65 49’

13 27 38 49 76 97 65 49’

13 27 38 49 49’

97 65 76

13 27 38 49 49’

65 97 76

13 27 38 49 49’

65 76 97

例例 9.5 9.5 直接选择排序过程示例直接选择排序过程示例初始关键字

第一趟排序后

第二趟排序后

第三趟排序后

第四趟排序后

第五趟排序后

第六趟排序后

第七趟排序后得最后排序结果

Page 34: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

直接选择排序算法直接选择排序算法void SelectSort(SeqList R){ int i , j , k ; for(i=1;i<n;i++) { k=i ; for(j=i+1;j<=n;j++) if(R[j].key<R[k].key) k=j; if(k!=i) { R[0]=R[i] ; R[i]=R[k] ; R[k]=R[0] ; } }}

Page 35: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

直接选择排序的算法分析直接选择排序的算法分析

当文件为正序时,移动次数为当文件为正序时,移动次数为 00 ;当文件初;当文件初态为反序时,每趟排序均要执行交换操作,总态为反序时,每趟排序均要执行交换操作,总的移动次数取最大值的移动次数取最大值 3(n-1)3(n-1) 。。直接选择排序的时间复杂度为直接选择排序的时间复杂度为 O(nO(n22)) 。。直接选择排序的空间复杂度为直接选择排序的空间复杂度为 O(1)O(1) 。。直接选择排序是不稳定的排序方法,见例 直接选择排序是不稳定的排序方法,见例 9.9.55 。。

无论初始状态如何,在第无论初始状态如何,在第 i i 趟排序中选择趟排序中选择最小关键字的记录,需做最小关键字的记录,需做 n-in-i 次比较,因此次比较,因此总的比较次数为:总的比较次数为:

1

1

2 )(2/)1()(n

i

nOnnin

Page 36: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

堆的定义堆的定义 :: nn 个关键字序列称为堆,当且仅当该序列满足如下关系:个关键字序列称为堆,当且仅当该序列满足如下关系:

或或122 iiii KKKK 且

从堆的定义可以看出,堆实质上是满足如下从堆的定义可以看出,堆实质上是满足如下性质的二叉树:性质的二叉树:树中任一非叶结点的关键字均不大于树中任一非叶结点的关键字均不大于 (( 或或不小于不小于 )) 其左右孩子其左右孩子 (( 若存在的话若存在的话 )) 结点的关键字。结点的关键字。

9.4.2 9.4.2 堆排序堆排序

122 iiii KKKK 且

)2/1( ni

)2/1( ni

Page 37: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

10

15 56

25 30 70

10

15

56

25

30

70

小根堆示例小根堆示例

小根堆:根结点(即堆顶)的关键字是堆里所有结点关键字中最小者的堆,称为小根堆。

Page 38: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

70

56 30

25 15 10

70

56

30

25

15

10

大根堆示例大根堆示例

大根堆:根结点(即堆顶)的关键字是堆里所有结点关键字中最大者的堆,称为大根堆。

Page 39: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

堆排序及其特点堆排序及其特点 堆排序是一种树型选择排序。堆排序是一种树型选择排序。 在排序过程中,将在排序过程中,将 R[1]R[1] 到到 R[n]R[n] 看成是看成是

一棵完全二叉树的顺序存储结构,利用一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键的内在关系,在当前无序区中选择关键字最大字最大 (( 或最小或最小 )) 的记录。的记录。

堆排序分为两个步骤:堆排序分为两个步骤: 11 、根据初始输入,形成初始堆。、根据初始输入,形成初始堆。 22 、通过一系列的记录交换和重新调整、通过一系列的记录交换和重新调整 进行排序。进行排序。

Page 40: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

堆排序利用了大根堆 ( 或小根堆 )堆顶记录的关键字最大 ( 或最小 )这一特征,使得在当前无序区中选取最大 ( 或最小 ) 关键字的记录变得简单。用大根堆排序的基本思想

① 先将初始文件 R[1..n]建成一个大根堆,此堆为初始的无序区;

② 再将关键字最大的记录 R[1]( 即堆顶 ) 和无序区的最后一个记录 R[n] 交换,由此得到新的无序区 R[1..n-1] 和有序区 R[n] ,且满足 R[1..n-1].keys≤R[n].key ;

③ 由于交换后新的根 R[1] 可能违反堆的定义,故应将当前无序区 R[1..n-1] 重新调整为堆。然后再次将 R[1..n-1] 中关键字最大的记录R[1] 和该区间的最后一个记录 R[n-1] 交换,由此得到新的无序区 R[1..n-2] 和有序区 R[n-1..n] ,且仍满足关系 R[1..n-2].keys≤R[n-1..n].keys ,同样要将 R[1..n-2] 重新调整为堆。…,直到无序区只有一个元素为止。

堆排序算法堆排序算法

Page 41: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

注意:注意:

①① 只需做只需做 n-1n-1 趟排序,选出较大的趟排序,选出较大的 n-1n-1个关键字即可以使得文件递增有序。个关键字即可以使得文件递增有序。

②② 用小根堆排序与利用大根堆类似,只用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻,堆和直接选择排序相反:在任何时刻,堆排序中无序区总是在有序区之前,且有排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩序区是在原向量的尾部由后往前逐步扩大至整个向量为止。大至整个向量为止。

Page 42: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.7 9.7 堆排序算法堆排序算法void HeapSort(SeqList R)

{/* 对 R[1..n] 进行堆排序,不妨用 R[0]做暂存单元 */

int i ; BuildHeap(R) ; /*建堆函数,将 R[1..n]建成初始堆 */

for(i=n ; i>1 ; i--)

{R[0]=R[1] ; R[1]=R[i] ; R[i]=R[0] ; /* 将堆顶和堆中最后一个记录交换 */

Heapify(R , 1 , i-1) ; /*调整堆函数 , 将 R[1..i-1] 重新调整为堆 */

}

}

Page 43: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

①调整堆函数 Heapify( ) 的实现

每趟排序开始前 R[l..i] 是以 R[1] 为根的堆,在 R[1]与 R[i] 交换后,新的无序区 R[1..i-1] 中只有 R[1] 的值发生了变化,故除 R[1] 可能违反堆性质外,其余任何结点为根的子树均是堆。因此,当被调整区间是R[low..high] 时,只须调整以 R[low] 为根的树即可。

R[low] 的左、右子树 ( 若存在 )均已是堆,这两棵子树的根 R[2low]和 R[2low+1]分别是各自子树中关键字最大的结点。若 R[low].key 不小于这两个孩子结点的关键字,则 R[low]未违反堆性质,以 R[low]为根的树已是堆,无须调整;否则必须将 R[low] 和它的两个孩子结点中关键字较大者进行交换,即:交换 R[low]与 R[large](R[large].key=max(R[2low].key , R[2low+1].key)) 。交换后又可能使结点 R[large] 违反堆性质,同样由于该结点的两棵子树 ( 若存在 )仍然是堆,故可重复上述的调整过程,对以 R[large] 为根的树进行调整。此过程直至当前被调整的结点已满足堆性质,或者该结点已是叶子为止。上述过程就象过筛子一样,把较小的关键字逐层筛下去,而将较大的关键字逐层选上来。因此,有人将此方法称为“筛选法”。

Page 44: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.8 9.8 调整堆函数(用筛选法调整堆)。调整堆函数(用筛选法调整堆)。void Heapify(SeqList R,int k,int m){/* 假设 R[k..m] 是以 R[k] 为根的完全二叉树 ,且分别以 R[2k] 和 R[2k+1] 为根的左、右子树 */ /* 为大根堆 ,调整 R[k], 使整个序列 R[k..m]满足堆的性质 */

RecType t=r[k] ; /*暂存“根”结点 R[k]*/ x=r[k].key ; i=k ; j=2*i ; while(j<=m) /*j≤m,R[j] 是 R[i] 的左孩子 */ { if((j<m) && (R[j].key< R[j+1].key)) j=j+1; /* 若存在右子树,且右子树根的关键字大,则沿右分支“筛选” */ if(x<R[j].key) /*孩子结点的关键字较大 */ {R[i]=R[j] ; /* 将 R[j] 换到双亲位置上 */ i=j ; /*修改当前被调整结点,并递推向下调整 */ j=2*i ; } else break ; /*调整完毕,退出循环 */ } R[i]=t ; /* 将最初的被调整结点 R[k]填入到恰当的位置 */ }(输出堆顶元素并调整建新堆的过程请参见后面的堆排序的全过程示例)

Page 45: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

②建堆函数 BuildHeap()的实现

要将初始文件 R[l..n]调整为一个大根堆,就必须将它所对应的完全二叉树中以每一结点为根的子树都调整为堆。显然只有一个结点的树是堆,而在完全二叉树中,所有序号 的结点都是叶子,因此以这些结点为根的子树均已是堆。这样,我们只需依次将以序号为 , -1 ,…, 1 的结点作为根的子树都调整为堆即可。

/ 2i n

/ 2n / 2n

Page 46: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法 9.9 建立大根堆算法(建堆函数)

Void BuildHeap(SeqList R)

{/*利用筛选法将初始文件 R[1..n]调整为一个大根堆 */

int i;

for(i=n/2;i>=1;i--)

Heapify(R,i,n); /*调整堆函数 */

}

Page 47: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

例例 9.8 9.8 用筛选法建新堆示例。用筛选法建新堆示例。 已知关键字序列为 关键字序列为 4242 ,, 1313 ,, 9191 ,, 2323 , , 2424 , , 1616 ,, 0505 ,, 8888 ,要求建立大根堆。因,要求建立大根堆。因 n=8n=8 ,故从第,故从第 44 个结个结点开始调整。点开始调整。

42

13

91

23

24

16

05

8842 13 91 23 24 16 05 88

(a) i=4 , 23筛下一层

Page 48: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

42

13

91

88

24

16

05

2342 13 91 88 24 16 05 23

(b) i=3(b) i=3 ,不调整,不调整

Page 49: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

42

13

91

88

24

16

05

2342 13 91 88 24 16 05 23

(c) i=2(c) i=2 ,, 1313 筛下两筛下两层层

Page 50: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

42

88

91

23

24

16

05

1342 88 91 23 24 16 05 13

(d) i=1(d) i=1 ,, 4242 筛下一层筛下一层

Page 51: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

91

88

42

23

24

16

05

1391 88 42 23 24 16 05 13

(e) (e) 建成的大根堆建成的大根堆

Page 52: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

建成大根堆后,将堆顶记录 R[1]与最后一个记录 R[n] 交换,就得到第一趟排序的结果;然后将剩余记录R[1.. n-1] 重新调整为堆,再将堆顶记录 R[1] 与最后一个记录 R[n-1] 交换,就得到第二趟排序的结果; … 如此重复 n-1 趟排序之后,就使有序区扩充到整个记录区 R[1] 到 R[n] 。 请参见下例——堆排序的全过程堆排序的全过程示例示例。

Page 53: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

91

88

42

23

24

16

05

1391 88 42 23 24 16 05 13

( a)初始堆 R[1]到 R[8]

堆排序的全过程示例堆排序的全过程示例

Page 54: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

13

88

42

23

24

16

05

9113 88 42 23 24 16 05 91

( b)第一趟排序之后

Page 55: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( c)重建的堆 R[1]到 R[7]

88

24

42

23

13

16

05

9188 24 42 23 13 16 05 91

Page 56: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

05

24

42

23

13

16

88

9105 24 42 23 13 16 88 91

( d)第二趟排序之后

Page 57: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( e)重建的堆 R[1]到 R[6]

42

24

16

23

13

05

88

9142 24 16 23 13 05 88 91

Page 58: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( f)第三趟排序之后

05

24

16

23

13

42

88

9105 24 16 23 13 42 88 91

Page 59: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( g)重建的堆 R[1]到 R[5]

24

23

16

05

13

42

88

9124 23 16 05 13 42 88 91

Page 60: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( h)第四趟排序之后

13

23

16

05

24

42

88

9113 23 16 05 24 42 88 91

Page 61: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( i)重建的堆 R[1]到 R[4]

23

13

16

05

24

42

88

9123 13 16 05 24 42 88 91

Page 62: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( j)第五趟排序之后

05

13

16

23

24

42

88

9105 13 16 23 24 42 88 91

Page 63: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( k)重建的堆 R[1]到 R[3]

16

13

05

23

24

42

88

9116 13 05 23 24 42 88 91

Page 64: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( l )第六趟排序之后

05

13

16

23

24

42

88

9105 13 16 23 24 42 88 91

Page 65: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( m)重建的堆 R[1]到 R[2]

13

05

16

23

24

42

88

9113 05 16 23 24 42 88 91

Page 66: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

( n)第七趟排序之后,整个记录区已经有序 , 堆排序到此结束。

05

13

16

23

24

42

88

9105 13 16 23 24 42 88 91

Page 67: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

堆排序的算法分析堆排序的算法分析

堆排序的时间复杂度为堆排序的时间复杂度为 O(n logO(n log22n)n) 。。

堆排序的空间复杂度为 堆排序的空间复杂度为 O(1)O(1) 。。 堆排序是不稳定的排序方法。堆排序是不稳定的排序方法。 由于建立初始堆所需要的比较次数较多,因此由于建立初始堆所需要的比较次数较多,因此

堆排序不适宜记录数较少的文件。堆排序不适宜记录数较少的文件。

Page 68: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.5 9.5 归并排序归并排序 归并排序的基本思想是:将一些有序的子序

列进行归并,从而得到有序的序列。 所谓归并,是指将若干个已排好序的子序列合并成一个有序的序列。归并是一种常见运算,其方法是:比较各子序列的第一个记录的键值,最小的一个就是排序后序列的第一个记录的键值。取出这个记录,继续比较各子序列现在的第一个记录的键值,便可找出排序后的第二记录。如此继续下去,最终可以得到排序结果。因此,归并排序的基础是归并。

Page 69: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

二路归并的基本思想二路归并的基本思想 设两个有序的子文件放在同一向量中相邻的位置设两个有序的子文件放在同一向量中相邻的位置上:上: R[low..m]R[low..m] ,, R[m+1..high]R[m+1..high] ,先将它们合并,先将它们合并到一个局部的暂存向量到一个局部的暂存向量 RR11 中,待合并完成后将中,待合并完成后将 RR11

复制回复制回 R[low..high]R[low..high] 中。中。

合并过程中,设置合并过程中,设置 ii ,, jj 和和 pp 三个指针,其初值三个指针,其初值分别指向这三个记录区的起始位置。合并时依次比分别指向这三个记录区的起始位置。合并时依次比较较 R[i]R[i] 和和 R[j]R[j] 的关键字,取关键字较小的记录复制的关键字,取关键字较小的记录复制到到 RR11[p][p] 中,然中,然后将被复制记录的指针后将被复制记录的指针 ii 或或 jj 加加 11 ,,并将指向复制位置的指针并将指向复制位置的指针 pp 加加 11 。。

重复这一过程直至两个输入的子文件有一个已全重复这一过程直至两个输入的子文件有一个已全部复制完毕部复制完毕 (( 不妨称其为空不妨称其为空 )) ,此时将另一非空的子,此时将另一非空的子文件中剩余记录依次复制到文件中剩余记录依次复制到 RR11 中即可。中即可。

Page 70: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

二路归并的示例二路归并的示例

25 57 48 37 12 92 86

25 57 37 48 92 12 86

25 37 48 57 12 86 92

12 25 37 48 57 86 92

Page 71: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.10 9.10 归并算法归并算法void Merge(SeqList R , int low , int m , int high){/* 将两个有序的子文件 R[low..m] 和 R[m+1..high]归并成一个有序的子文件 R[low..high] */

int i=low , j=m+1 , p=0 ;RecType *R1 ;R1=(RecType *)malloc((high-low+1)*sizeof(RecType)) ;if(! R1) {printf(“ 空间不足 !");return ERROR;}while(i<=m&&j<=high) R1[p++]=(R[i].key<=R[j].key)?R[i++] : R[j++]) ;while(i<=m) R1[p++]=R[i++] ;while(j<=high) R1[p++]=R[j++] ;for(p=0 , i=low ; i<=high ; p++ , i++) R[i]=R1[p] ;return OK;}

Page 72: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

二路归并二路归并的基本思想是:第的基本思想是:第 11 趟归并排序时,趟归并排序时,将待排序的文件将待排序的文件 R[1..n]R[1..n] 看作是看作是 nn 个长度为个长度为 11的有序子文件,将这些子文件两两归并,若的有序子文件,将这些子文件两两归并,若 nn为偶数,则得到 个长度为为偶数,则得到 个长度为 22 的有序子文件;的有序子文件;若若 nn 为奇数,则最后一个子文件轮空为奇数,则最后一个子文件轮空 (( 不参与不参与归并归并 )) 。故本趟归并完成后,前。故本趟归并完成后,前个有序子文件长度为个有序子文件长度为 22 ,但最后一个子文件长,但最后一个子文件长度仍为度仍为 11 ;第;第 22 趟归并则是将第趟归并则是将第 11 趟归并所趟归并所得到的 个有序的子文件两两归并,如此反得到的 个有序的子文件两两归并,如此反复,直到最后得到一个长度为复,直到最后得到一个长度为 nn 的有序文件的有序文件为止。为止。

上述每次的归并操作,都是将两个子序列归上述每次的归并操作,都是将两个子序列归并为一个子序列,这就是“并为一个子序列,这就是“二路归并二路归并”,类似”,类似地还可以有“地还可以有“三路归并三路归并”或“”或“多路归并多路归并”。”。

/ 2n

n2log

n2log

Page 73: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

归并过程示例归并过程示例

(25) (57) (48) (37) (12) (92) (86)

(25 57) (37 48) (12 92) (86)

(25 37 48 57) (12 86 92)

(12 25 37 48 57 86 92)

Page 74: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.11 9.11 一趟归并算法一趟归并算法void MergePass(SeqList R , int length){ int i ; for(i=1;i+2*length-1<=n;i=i+2*length) Merge(R , i , i+length-1 , i+2*length-1) ; if(i+length-1<n) Merge(R , i , i+length-1 , n) ; }

算法算法 9.12 9.12 二路归并排序算法二路归并排序算法void MergeSort(SeqList R){ int length ; for(1ength=1 ; length<n ; length*=2) MergePass(R , length) ;}

Page 75: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

归并排序的算法分析归并排序的算法分析

归并排序是稳定的排序方法。归并排序是稳定的排序方法。

归并排序在第归并排序在第 i i 趟归并后,有序趟归并后,有序子文件长度为子文件长度为 22ii ,因此,对于具有,因此,对于具有 nn 个记个记录的文件来说,必须做 趟归并,录的文件来说,必须做 趟归并,每趟归并所花的时间为每趟归并所花的时间为 O(n)O(n) ,所以,二路,所以,二路归并排序算法的时间复杂度为归并排序算法的时间复杂度为 O(nlogO(nlog22n)n) 。。 算法中辅助数组算法中辅助数组 R1R1 所需的空间所需的空间为为 O(n)O(n) 。(见算法。(见算法 9.10 9.10 ))

n2log

Page 76: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.6 9.6 基数排序基数排序 前面介绍的各种排序方法都是根据前面介绍的各种排序方法都是根据

关键字值的大小来进行排序的。本关键字值的大小来进行排序的。本节介绍的节介绍的基数排序基数排序是一种借助于多是一种借助于多关键字排序的思想对单个逻辑关键关键字排序的思想对单个逻辑关键字进行排序的方法。字进行排序的方法。

基数 排 序基数 排 序 的基本思想:采用 “分的基本思想:采用 “分配”和“收集”两种操作,对单逻配”和“收集”两种操作,对单逻辑关键字进行排序的一种方法辑关键字进行排序的一种方法。。

Page 77: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.6.1 9.6.1 多关键字的排序多关键字的排序

对扑克牌的排序对扑克牌的排序 每张扑克牌有两个“关键字”:花色和面值,且每张扑克牌有两个“关键字”:花色和面值,且

“花色”地位高于“面值”。“花色”地位高于“面值”。 可以先按“花色”排序(分成可以先按“花色”排序(分成 44 堆),再按堆),再按

“面值”整理排序。“面值”整理排序。 也可以先按“面值”排序(分成也可以先按“面值”排序(分成 1313 堆),再按堆),再按

“花色”整理排序。“花色”整理排序。 这两种整理扑克牌的方法就是两种多关键字的排这两种整理扑克牌的方法就是两种多关键字的排

序方法。序方法。

具体例子如下:具体例子如下:

Page 78: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.6.2 9.6.2 链式基数排序链式基数排序

基数排序基数排序是利用“分配”和“收集”是利用“分配”和“收集”两种操作对单关键字进行排序一种内两种操作对单关键字进行排序一种内部排序方法。部排序方法。

基数排序的基数排序的排序过程:设关键字共有排序过程:设关键字共有dd 位,分别令位,分别令 j= d-1, d-2,...,0, j= d-1, d-2,...,0, 依次依次执行执行 dd 次“分配”与“收集”。次“分配”与“收集”。

Page 79: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

179

208

306

093

859

984

055

009

271

033

B[0].f B[1].f B[2].f B[3].f B[4].f B[5].f B[6].f B[7].f B[8].f B[9].f

B[0].e B[1].e B[2].e B[3].e B[4].e B[5].e B[6].e B[7].e B[8].e B[9].e

271

093

033

984

055

306

208

179

859

009

(a) 初始关键字状态

(b) 第一趟分配之后,按最低位关键字(个位数字)分配

图 9.11 链式基数排序示例

Page 80: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

271

033

093

984

055

306

208

009

859

179

B[0].f B[1].f B[2].f B[3].f B[4].f B[5].f B[6].f B[7].f B[8].f B[9].f

B[0].e B[1].e B[2].e B[3].e B[4].e B[5].e B[6].e B[7].e B[8].e B[9].e

033

984

009

208

306

859

055

179

271

093

图 9.11 链式基数排序示例(续 1 )

(c) 第一趟收集之后

(d) 第二趟分配之后,按次低位关键字(十位数字)分配

Page 81: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

306

208

009

033

055

859

271

179

984

093

B[0].f B[1].f B[2].f B[3].f B[4].f B[5].f B[6].f B[7].f B[8].f B[9].f

B[0].e B[1].e B[2].e B[3].e B[4].e B[5].e B[6].e B[7].e B[8].e B[9].e

179

306

984

859

093

055

033

009

271

208

009

033

055

093

179

208

271

306

859

984

(f) 第三趟分配之后,按最高位关键字(百位数字)分配

(e) 第二趟收集之后

(g) 第三趟收集之后,得到有序的序列

图 9.11 链式基数排序示例(续 2 )

Page 82: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法的静态链表类型定义算法的静态链表类型定义#define RADIX 10 /* 关键字基数 */#define KEY_SIZE 6 /* 关键字项数的最大值 */#define LIST_SIZE 1000 /*静态链表的最大空间 */ typedef int KeyType ;typedef struct {KeyType keys[KEY_SIZE] ; /*子关键字数组 */ OtherType other_data ; /* 其它数据项 */ int next ; /*静态链域 */ }RecordType1 ; /*静态链表的结点类型 */ typedef struct {RecordType1 r[LIST_SIZE+1] ; /* r[0] 为头结点 */ int length ; /*静态链表的当前长度 */ int keynum ; /* 记录的当前关键字个数 */ }SlinkList ; /*静态链表类型 */ typedef int PVector[RADIX] ; /*指针数组类型 */

Page 83: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.14 9.14 分配算法分配算法void Distribute(RecordType1 r[],int i,PVector head,PVector tail){/* 记录数组 r 中记录已按低位关键字 key[i+1] ,…, key[d] 进行过“低位优先”排序。 *//* 本算法按第 i 位关键字 key[i]建立 RADIX个队列,同一个队列中记录的 key[i] 相同 *//*head[j] 和 tail[j]分别指向各队列中第一个和最后一个记录( j=0 , 1 , 2 ,…, RADIX-1 ) */

/*head[j]=0 表示相应队列为空队列 */ for(j=0 ; j<=RADIX-1 ; ++j) head[j]=0 ; /* 将 RADIX个队列初始化为空队列 */ p=r[0].next ; /*p指向链表中的第一个记录 */ while(p!=0) { j=Order(r[p].key[i]) ; /* 用记录中第 i 位关键字求相应队列号 */ if(head[j]==0) head[j]=p ; /* 将 p 所指向的结点加入第 j 个队列中 */ else r[tail[j]].next=p ; tail[j]=p ; p=r[p].next ; }}

Page 84: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.15 9.15 收集算法收集算法void Collect(RecordType r[] , PVector head , PVector tail){/* 本算法从 0 到 RADIX-1 扫描各队列,将所有非空队列首尾相接,重新链接成一个链表 */ j=0 ; while(head[j]==0) /*寻找第一个非空队列 */ ++j ; r[0].next =head[j] ; t=tail[j] ; while(j<RADIX-1) /*寻找并串接所有非空队列 */ { ++j ; while((j<RADIX-1)&&(head[j]==0)) /*找下一个非空队列 */ ++j ; if(head[j]!=0) /*链接非空队列 */ { r[t].next =head[j] ; t=tail[j] ; } } r[t].next =0 ; /*t指向最后一个非空队列中的最后一个结点 */ }

Page 85: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

算法算法 9.16 9.16 基数排序算法基数排序算法void RadixSort(RecordType r[],int length){/*length 个记录存放在数组 r 中,执行本算法进行基数排序后,链表中的记录将按 *

/

/*关键字从小到大的顺序相链接 */ n=length ; for(i=0 ; i<=n-1 ; ++i) r[i].next=i+1 ; /*构造静态链表 */ r[n].next=0 ; d=keynum ; for(i=d-1 ; i>=0 ; --i) /* 从最低位子关键字开始,进行 d 趟分配和收集 */ { Distribute(r , i , head , tail) ; /* 第 i 趟分配 */ Collect(r , head , tail); /* 第 i 趟收集 */ }}

Page 86: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

基数排序的算法分析基数排序的算法分析基数排序所需的计算时间不仅与文件的大基数排序所需的计算时间不仅与文件的大

小小 nn 有关,而且还与关键字的位数有关,而且还与关键字的位数 dd 、关、关键键

字的基数字的基数 rr 有关。基数排序的时间复杂度有关。基数排序的时间复杂度为为

O(d(n+r))O(d(n+r)) 。。基数排序所需的辅助存储空间为基数排序所需的辅助存储空间为 O(n+rd)O(n+rd) 。。基数排序是稳定的。基数排序是稳定的。

Page 87: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.7 9.7 内部排序算法比较内部排序算法比较

选取排序方法时需要考虑的因素有:选取排序方法时需要考虑的因素有: 待排序的记录数目待排序的记录数目 记录本身信息量的大小记录本身信息量的大小 关键字的结构及其分布情况关键字的结构及其分布情况 对排序稳定性的要求对排序稳定性的要求 语言工具的条件、辅助空间的大小语言工具的条件、辅助空间的大小

Page 88: 基本概念 插入排序 交换排序 选择排序 归并排序 基数排序 内部排序比较 外排序

9.8 9.8 外部排序简介外部排序简介 **

如果待排序的记录数很大,无法如果待排序的记录数很大,无法将所有记录都调入内存,那么只能将它们将所有记录都调入内存,那么只能将它们存放在外存上,我们称这时的排序为存放在外存上,我们称这时的排序为外部外部排序排序。。

外部排序主要是依靠数据的内外外部排序主要是依靠数据的内外存交换和“内部归并”等方法来实现的 。存交换和“内部归并”等方法来实现的 。