1、1,4.2 排 序 4.2.1 概 述,1、排序的功能:将一个数据元素(或记录)的任意序列,重新排成一个按关键字有序的序列。 2、排序过程的组成步骤:首先比较两个关键字的大小;然后将记录从一个位置移动到另一个位置。 3、排序的分类:若排序时待排序记录存放在内存中进行排序,则称为内部排序;若待排序记录数量很大,在内存中一次不能容纳全部记录,需要在排序过程中对外存进行访问,则称为外部排序。,2,4、排序方法,插入排序,选择排序,交换排序,归并排序,直接插入排序,折半插入排序 希尔排序 表插入排序,直接选择排序,堆排序,冒泡排序,快速排序,5、排序的稳定性对任意的数据元素序列,使用某个排序方法,对它
2、按关键字进行排序:若相同关键字元素间的位置关系,排序前与排序后保持一致,称此排序方法是稳定的;而不能保持一致的排序方法则称为不稳定的。,3,假设待排序的记录存放在地址连续的一组存储单元L中,那么这种存储方式下的数据类型可描述为:,typedef struct ElemType rMAX+1; / 数组int length; /表长度 STBL; STBL L;,根据需要定义元素类型比如 typedef struct int key; / 关键字 int info; / 其他信息分量 ElemType;,4,4.2.2 插入排序 直接插入、折半插入、希尔排序,1、直接插入排序: 基本思想:从数组
3、的第2号元素开始,顺序从数组中取出元素,并将该元素插入到其左端已排好序的数组的适当位置上,待排元素序列:53 27 36 15 69 42第1次排序: 27 53 36 15 69 42第2次排序: 27 36 53 15 69 42第3次排序: 15 27 36 53 69 42第4次排序: 15 27 36 53 69 42第5次排序: 15 27 36 42 53 69,直接插入排序示例,5,void InsertSort(STBL *p) int i, j; for(i=2; ilength; i+)if(p-ri.key ri-1.key) / 小于时,需将ri插入有序表 p-r0.
4、key=p-ri.key; / 为统一算法设置监测for(j=i-1; p-r0.key rj.key; j-) / 从后向前找p-rj+1.key = p-rj.key; / 记录后移p-rj+1.key = p-r0.key; / 插入到正确位置 ,直接插入算法如下: 方法:Ki与Ki-1,K i-2,K1依次比较,直到找到应插入的位置。,6,例,49 38 65 97 76 13 27,i=2 38 (38 49) 65 97 76 13 27,i=3 (38 49 65) 97 76 13 27,i=4 (38 49 65 97) 76 13 27,i=5 76 (38 49 65 7
5、6 97) 13 27,i=6 13 (13 38 49 65 76 97) 27,( ),i=7 (13 38 49 65 76 97) 27,27,97,76,65,49,38,27,void InsertSort(STBL *p) int i, j; for(i=2;ilength;i+)if(p-ri.key ri-1.key) p-r0.key = p-ri.key; / 设置监测for(j=i-1;p-r0.key rj.key;j-) p-rj+1.key = p-rj.key; / 记录后移p-rj+1.key = p-r0.key; / 插入位置 ,对于有n个数据元素的待排序
6、列,插入操作要进行n-1次,r0 r1 r2 r3 r4 r5 r6 r7,直接插入排序是一个稳定的排序方法。,7,效率分析空间效率:仅用了一个辅助单元。时间效率:向有序表中逐个插入记录的操作,进行了n-1趟,每趟操作分为比较关键字和移动记录,而比较的次数和移动记录的次数取决于待排序列按关键字的初始排列。,最好情况:记录按关键字从小到大排列(正序/升序),每趟操作只需1次比较。 总比较次数= n-1 最坏情况:记录按关键字从大到小排列(逆序/降序),第j趟操作,插入记录需要同前面的j个记录进行j次关键字比较,移动记录的次数为j+2次。,总比较次数=,总移动次数=,平均情况:即第j趟操作,插入记
7、录约同前面的j/2个记录进行关键字比较,移动记录的次数为j/2+2次。,总比较次数=,总移动次数=,该算法适合于n较小的情况时间复杂度为O(n2),8,2、折半插入排序,折半插入排序的条件:在有序序列中插入一个关键字。,4.2.2 插入排序直接插入、折半插入、希尔排序,基本思想:折半插入排序在寻找插入位置时,不是逐个比较而是利用折半查找的原理寻找插入位置 。 待排序元素越多,改进效果越明显。,9,二分判定有序表插入位置方法: low=1; high=j-1; r0=rj; /* 有序表长度为j-1,第j个记录为待插入记录,设置有序表区间,待插入记录送辅助单元 */ 若lowhigh,得到插入位
8、置,转 lowhigh, m=(low+high)/2; /* 取表的中点,并将表一分为二,确定待插入区间 */ 若r0.key rm.key,high=m-1; / 插入位置在低半区否则,low=m+1; / 插入位置在高半区转 high+1为待插入位置,从j-1到high+1的记录,逐个后移,rhigh+1=r0 / 放置待插入记录,2、折半插入排序,4.2.2 插入排序 直接插入、折半插入、希尔排序,10,(highlow ,查找结束,插入位置为low或high+1 ),( 4236 ),( 4253 ),11,效率分析,确定插入位置所进行的折半查找,关键字的比较次数至多为,时间复杂度仍
9、为O(n2),是一个稳定的排序方法。,2、折半插入排序,4.2.2 插入排序 直接插入、折半插入、希尔排序,折半插入排序移动记录的次数和直接插入排序相同。,12,希尔排序又称缩小增量排序,是1959年由D.L.Shell提出来的,较前述几种插入排序方法有较大的改进。,3、希尔排序,4.2.2 插入排序 直接插入、折半插入、希尔排序,希尔排序方法: 1)选择一个步长序列d1,d2,dk,其中didj (ij), dk=1; 2)按步长序列个数k,对序列进行k趟排序; 3)每趟排序,根据对应的步长di,将待排序列分割成若干子序列,分别对各子表进行直接插入排序。仅步长因子为1时,整个序列作为一个表来
10、处理,表长度即为整个序列的长度。,13,14,详细排序过程:,步长因子 d = 5,3,1 ;,49,13,38,27,27,4,55,38,65,48,97,55,76,4,15,算法描述如下: void ShellInsert(STBL *p, int dk) / 一趟增量为dk的插入排序 int i, j;for(i=dk+1; ilength; i+) /* dk为步长因子 */if(p-elemi.key elemi-dk.key) / 把elemi插入有序表 p-elem0 = p-elemi; / 为统一算法设置监测for(j=i-dk; j0 / 调用一趟增量为dltak的插入
11、排序 ,16,希尔排序特点 (1)子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列; (2)希尔排序可提高排序速度,因为分组后n值减小,n更小,而T(n)=O(n),所以T(n)从总体上看是减小了; (3)关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序。,3、希尔排序,4.2.2 插入排序 直接插入、折半插入、希尔排序,17,效率分析,希尔排序时效分析很难,关键字的比较次数与记录移动次数依赖于步长因子序列的选取,特定情况下可以准确估算出关键字的比较次数和记录的移动次数。,是一个不稳定的排序方法。,3、希尔排序,4.2.2 插入排序 直接
12、插入、折半插入、希尔排序,步长因子序列有多种取法,有取奇数的,也有取质数的。 注意:步长因子中除1外没有公因子,且最后一个步长因子必须为1。,18,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两种。1、冒泡排序(起泡排序) 思想:小的浮起,大的沉底。从左端开始比较。 第1趟:第1个与第2个比较,大则交换;第2个与第3个比较,大则交换,关键字最大的记录交换到最后一个位置上; 第2趟:对前n-1个记录进行同样的操作,关键字次大的记录交换到第n-1个位置上;依次类推,则完成排序。,19,9 8 5 4 2 0,9 8 5 4 2 0,8 9,5 9,4 9,2 9,0 9,9
13、,9,8 5 4 2 0 9,5 8,4 8,2 8,0 8,8,8,4 2 0 5 8 9,5,5,2 0 4 5 8 9,4,4,0 2 4 5 8 9,2,2,结果,1,2,3,4,开始,5,0 2 4 5 8 9,例 用冒泡法(下沉法)对 6 个数排序(由小到大),起泡法的思路是:将相邻两个数比较,将小的调到前头。,比较次数:5 4 3 2 1,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两种。 1、冒泡排序(起泡排序),20,/* 冒泡排序的C程序段:*/ void BubSort(STBL L, int n) int i, x, j;for (j=1; jn
14、; j+)for (i=1; i=n-j; i+)if (L.ri+1.keyL .ri.key) x=L.ri; L .ri=L .ri+1; L .ri+1=x; ,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两种。 1、冒泡排序(起泡排序),排序n个记录最多需要n-1趟冒泡排序,适合于数据较少的情况,21,效率分析,空间效率:仅用了1个辅助单元。 时间效率:总共要进行n-1趟冒泡,对j个记录的表进行一趟冒泡需要j-1次关键字比较。,总比较次数=,移动次数:最好情况下:待排序列已有序,不需移动。,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两
15、种。 1、冒泡排序(起泡排序),正序:时间复杂度为O(n) 逆序:时间复杂度为O(n2),22,2、快速排序 (对冒泡排序的改进) 思想:快速排序是通过比较关键字、交换记录,以某个记录为界(该记录称为支点),将待排序列按关键字划分成2部分。其中,一部分所有记录的关键字支点记录的关键字,另一部分所有记录的关键字支点记录的关键字。对各部分不断划分,直到整个序列按关键字有序。,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两种。,23,做法: 附设两个指针low和high,初值分别指向第1个记录和最后1个记录,设支点记录为r0 ,(r0通常取第1个记录的值为基准值。) 首先从h
16、igh所指位置起向前搜索,找到第1个小于基准值的记录与基准记录交换, 然后从low所指位置起向后搜索,找到第1个大于基准值的记录与基准记录交换, 重复这两步直至low=high为止。,4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两种。 2、快速排序 (对冒泡排序的改进),24,一趟快速排序过程示意图:,有序序列 6 18 23 52 67,r0.key,low,high,1次交换 18 52 6 67 23,low,high,2次交换 18 23 6 67 52,high,3次交换 18 6 23 67 52 / 完成一趟排序后分别进行快速排序,low,high,25,
17、完成一趟排序: ( 27 38 13) 49 (76 97 65 50),分别进行快速排序: ( 13) 27 (38) 49 (50 65) 76 (97),快速排序结束: 13 27 38 49 50 65 76 97,49,27,49,65,13,49,49,97,26,效率分析 时间复杂度 最好情况(每次总是选到中间值作枢轴)T(n)=O(nlog2n) 最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n),空间复杂度:需栈空间以实现递归 最坏情况:S(n)=O(n) 一般情况:S(n)=O(log2n),4.2.3 交 换 排 序 交换排序的特点在于交换。有冒泡和快速排序两
18、种。 2、快速排序 (对冒泡排序的改进),27,1、直接选择排序 思想:首先从1n个元素中选出关键字最小的记录交换到第1个位置上。然后再从第2 个到第n个元素中选出次小的记录交换到第2个位置上,依次类推。 时间复杂度为O(n2), 适用于待排序元素较少的情况。,4 . 2. 4 选择排序 直接选择排序、堆排序,28,4.2.4 选择排序 直接选择排序、堆排序。1、直接选择排序 思想:首先从1n个元素中选出关键字最小的记录交换到第1个位置上。然后再从第2 个到第n个元素中选出次小的记录交换到第2个位置上,依次类推。 时间复杂度为O(n2), 适用于待排序元素较少的情况。,初态,8 3 9 1 6
19、,8 3 9 1 6,8 3 9 1 6,8 3 9 1 6,1 3 9 8 6,1 3 9 8 6,1 3 9 8 6,29,直接选择排序的算法如下: void SelectSort( STBL L, int n) int i,j,k,t;for (i=1; i=n; +i)/ 选择第i小的元素,并交换到位 k=i;for(j=i+1; j=n; +j)if (L.rj.key L.rk.key) k=j; / rk中存放的是第i小的元素if(k!=i) / 交换 t=L.ri; L.ri=L.rk; L.rk=t; ,30,效率分析 时间复杂度 记录移动次数 最好情况:0 最坏情况:3(n
20、-1) 比较次数:,空间复杂度:S(n)=O(1),T(n)=O(n),4 . 2. 4 选择排序 1. 直接选择排序,31,4.2.4 选择排序2、堆排序是具有特定条件的顺序存储的完全二叉树,其特定条件是:任何一个非叶子结点的关键字大于等于(或小于等于)子女的关键字的值。 (1)堆定义:设有n个元素的序列 k1,k2,kn,当且仅当满足下述关系之一时,称之为堆。,i=1,2,n/2,32,4.2.4 选择排序2、堆排序是具有特定条件的顺序存储的完全二叉树,其特定条件是:任何一个非叶子结点的关键字大于等于(或小于等于)子女的关键字的值。 堆定义:设有n个元素的序列 k1,k2,kn,当且仅当满
21、足下述关系之一时,称之为堆。 (2) 堆的示例,(a)堆顶元素取最大值,(b)堆顶元素取最小值,33,4.2.4 选择排序2、堆排序 (3)堆排序思想:设有n个元素,将其按关键字排序。首先将这n个元素按关键字建成堆,将堆顶元素输出,得到n个元素中关键字最小(或最大)的元素。然后,再对剩下的n-1个元素建成堆,输出堆顶元素,得到n个元素中关键字次小(或次大)的元素。如此反复,便得到一个按关键字有序的序列。称这个过程为堆排序。,(4) 实现堆排序需解决两个问题:1) 如何由一个无序序列建成一个堆? 2) 输出堆顶元素后,如何将剩余元素调整成一个新的堆?,34,(5)第二个问题解决方法筛选,方法:输
22、出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”,35,(6)第一个问题解决方法 方法:从无序序列的第n/2个元素(即此无序序列对应的完全二叉树的最后一个非终端结点)起,至第一个元素止,进行反复筛选,(c): 49被筛选后的状态,(d): 56被筛选后的状态,(e): 被筛选之后建成堆,36,void HeapSort( HeapType *H) / 堆顺序表H进行堆排序for( i=H.length/2; i0; i)HeapAdjust(H, i,
23、H.length); / 把H.1H.length调整为一个堆,for(i=H.length; i1; i) / 把堆顶元素与最后一个记录相交换rc=H.r1; H.r1=H.ri; H.ri=rc;HeapAdjust(H, 1, i-1); / 把H.r1i-1重新调整为一个堆,F、堆排序算法,37,A:,F、堆排序算法,38,39,例 含8个元素的无序序列(49,38,65,97,76,13,27,50) 先建成堆,40, 进行堆排序,41,42,43,4.2.4 选择排序 直接选择排序、堆排序 2、堆排序,效率分析: 时间复杂度:最坏情况下 T(n)=O(nlog2n) 空间复杂度:S
24、(n)=O(1),44,4.2.5 归并排序,功能: 将2个或2个以上的有序表组成一个新的有序表。 思想: 把具有n个记录的表看成是n个有序的子表,每个子表的长度为1,然后两两归并,得到n/2个长度为2或为1的有序子表;再两两归并,如此重复,直到得到1个长度为n的有序表为止。,45,4.2.5 归并排序,初始序列 23 52 67 6 18 10一趟归并后 23 52 6 67 10 18二趟归并后 6 23 52 67 10 18三趟归并后 6 10 18 23 52 67,归并排序示例,46,4.2.5 归并排序,效率分析: 时间复杂度:T(n)=O(nlog2n) 空间复杂度:S(n)=
25、O(n),47,4.2.6 内部排序方法的选择,从时间复杂度和空间复杂度来看,我们所讨论的各种内部排序方法结果如下表所示。,选择排序、直接插入排序和冒泡排序属于简单排序方法。,在最好情况下,直接插入排序和冒泡排序最快; 在平均情况下,快速排序最快; 在最坏情况下,堆排序和归并排序最快。,48,各种排序方法各有优缺点,故在不同情况下可作不同的选择。通常需考虑的因素有:待排序的记录个数;记录本身的大小;记录的键值分布情况等。若待排序的记录个数n较小时,可采用简单排序方法。若n较大时,应采用快速排序或堆排序。若待排序的记录已基本有序,可采用起泡排序。,4.2.6 内部排序方法的选择,从稳定性看,简单排序方法是稳定的,而快速排序、堆排序和希尔排序等时间性能较好的排序方法都是不稳定的。,小结,1、了解排序的分类、种类及其稳定性2、掌握简单排序、希尔排序、堆排序、快速排序,50,作业:P178 第2、3、6题 实验:选择本章任意一种排序方法上机实现。 实验要求:包含输入模块、输出模块、排序模块、主函数模块,程序调试成功,用word写出实验报告。,