1、1,7.1 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,2,7.1 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,3,7.1 内部排序方式,几个基本概念: 数据表(datalist):一个待排序数据元素的有限集合,也被称为待排序表。 排序码(key):数据元素通常有多个属性域,其中有一个属性域可用来区分数据,作为排序的依据,该属性域即为排序码。 排序:将一组数据(数据表)按照排序码从小到
2、大(或从大到小)的顺序进行重新排列的过程。,4,7.1 内部排序方式,几个基本概念: 数据表(datalist):一个待排序数据元素的有限集合,也被称为待排序表。 排序码(key):数据元素通常有多个属性域,其中有一个属性域可用来区分数据,作为排序的依据,该属性域即为排序码。 排序:将一组数据(数据表)按照排序码从小到大(或从大到小)的顺序进行重新排列的过程。,5,7.1 内部排序方式,排序的确切定义: 设含有n个数据元素待排序表:R1, R2, , Rn,其相应的排序码序列为K1, K2, , Kn。 确定排序码下标1, 2, , n的一种排列p1, p2, pn,使得各排序码满足下列的非递
3、减(或非递增)关系:,Kp1Kp2Kpn,(或Kp1Kp2Kpn),6,7.1 内部排序方式,几个基本概念: 排序算法的稳定性 设排序码Ki = Kj (1in, 1jn, ij ),且在排序前的序列中Ri领先于Rj (即 i j)。 若在排序后的序列中Ri仍领先于Rj,则称所用的排序算法是稳定的;反之,则称所用的排序算法是不稳定的。,7,7.1 内部排序方式,几个基本概念: 排序算法的稳定性 设排序码Ki = Kj (1in, 1jn, ij ),且在排序前的序列中Ri领先于Rj (即 i j)。 若在排序后的序列中Ri仍领先于Rj,则称所用的排序算法是稳定的;反之,则称所用的排序算法是不稳
4、定的。,设排序前的数据序列为:52, 49, 80, 36, 14, 58, 36, 23 若排序后的数据序列为:14, 23, 36, 36, 49, 52, 58, 80, 则排序算法是稳定的。 若排序后的数据序列为:14, 23, 36, 36, 49, 52, 58, 80, 则排序算法是不稳定的。,8,7.1 内部排序方式,几个基本概念: 内部排序:若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。 本章所介绍的排序算法均为内部排序算法。 外部排序:若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。,9,7.1 内部排序方式,
5、内部排序的存储方式: 顺序存储:移动记录(数据)实现排序。 链式存储:修改指针实现排序。 本章主要讨论顺序存储结构下的各种内部排序算法。,内部排序的方法,逐步扩大记录的有序序列的过程。,有序序列区,无 序 序 列 区,有序序列区,无 序 序 列 区,10,7.1 内部排序方式,根据“扩大”有序序列长度的不同方式,可将内部排序算法分为以下几类: 插入类:插入排序 选择类:选择排序 交换类:交换排序 归并类:归并排序 其他类:基数排序,11,7.1 内部排序方式,在排序过程中,一般进行以下两种基本操作: (1)比较两个排序码(关键字)的大小; (2)将数据元素从一个位置移动到另一个位置。 说明:
6、对于顺序存储结构,操作(1)和(2)都是必须做的本章这两种操作都考虑。 对于链式存储结构,操作(1)是必须做的,而操作(2)可以避免。,12,7.1 内部排序方式,排序算法的效率: 时间开销 排序算法的时间开销是衡量排序算法好坏的最重要的标志。 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量,通常选取二者中较大值作为排序算法的时间复杂度。 时间开销通常按平均情况考虑,有时也需按最好情况或者最坏情况考虑。 空间开销 排序算法执行时所需的附加空间。 评价排序算法好坏的另一个标准。,13,待排序数据表的类定义: class Element /数据元素的定义KeyType key; /
7、排序码field otherdata; /其它数据成员public:KeyType Key ( ) return key; /提取排序码void setKey (KeyType x ) key = x; /修改排序码; /Element,7.1 内部排序方式,14,class datalist /数据表的定义Element R ; /待排序数组, 下标为零的元素不存储int MaxSize; /最大元素个数int CurrentSize; /当前元素个数public:datalist ( int MaxSz = 100) /构造函数MaxSize = Maxsz;CurrentSize = 0
8、;R= new ElementMaxSz;,15,void swap ( Element /堆排序 /datalist,16,7.1 内部排序方式,特别说明 在本章中,对含有n个元素的数据表,需开辟一个大小为n+1的数组,下标为零的元素不存储数据,而是作为“监视哨”(排序时用于数据比较和交换)。,17,7.1 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,18,7.2 插入排序,插入排序(Insert Sorting) 基本思想:每次将一个待排序数据, 按其排序码大小,插入到前面已经排好序的
9、一组数据的适当位置上,直到全部数据插入为止。,共做n-1趟排序,第i趟排序的过程如下:,19,7.2 插入排序,插入排序(Insert Sorting) 包括以下三种具体方法: 直接插入排序 折半插入排序 希尔排序,20,直接插入排序 (Insert Sort),基本思想:当插入第i (i 2)个数据时,前面的R1, , Ri-1已经排好序。此时,用Ri的排序码与Ri-1, Ri-2, , R1的排序码依次进行比较,直至找到Rj.keyRi.key(1ji)并将Rj+1, , Ri-1向后顺移,将Ri插入到j+1位置处。 插入过程可分为两步: (1)可利用“顺序查找”实现“在R1i-1中查找R
10、i的插入位置”; (2)插入。,21,例:排序码序列T=(21,25,49,25*,16,08),直接插入排序的具体过程如下:,*表示后一个25,假设该序列已存入一个一维数组R7中,将R0作为缓冲或暂存单元(Temp),则算法执行过程为:,21,i=2,i=3,i=5,i=4,i=6,25,25,25,49,49,49,25*,49,16,16,08,49,初态:,16,25,21,16,完成!,考虑:若采用链式存储结构,直接插入排序是否可行? 直接插入排序不仅可行,而且还无需移动元素,时间效率更高!,22,直接插入排序算法如下: void InsertionSort( ) /按排序码Key非
11、递减顺序对数据表进行直接插入排序for(i=2; i=CurrentSize; i+ ) if (Ri.KeyRi-1.Key) R0=Ri; /待排序数据放入监视哨for(j=i-1; R0.KeyRj.Key; j-)Rj+1=Rj; /移动数据Rj+1=R0; /插入数据 /end if /end for /InsertionSort,23,设待排序数据的个数为n,则直接插入排序算法将执行n-1趟。 排序码比较次数和数据移动次数与数据排序码的初始状态有关。 最好情况(待排序列已按排序码从小到大有序): 每趟只需与前面有序序列的最后一个数据比较1次,不需要移动数据; 总的排序码比较次数为n
12、-1,数据移动次数为0。,直接插入排序的算法分析,24,最坏情况(待排序列中的排序码逆序有序): 第i趟时,第i个数据必须与前面i-1个数据都做排序码比较,并且每做1次比较就要做1次数据移动。 因此,总的排序码比较次数KCN (Key Comparison Number)和数据移动次数RMN (Element Move Number)分别为:,直接插入排序的算法分析,25,平均情况(可取上述最好情况和最坏情况的平均值) 总的排序码比较次数和数据移动次数均为:因此,直接插入排序算法的时间复杂度为O(n2)。 空间效率:只有一个辅助空间R0,即O(1)。 直接插入排序是一种稳定的排序方法。,直接插
13、入排序的算法分析,26,折半插入排序 (Binary Insertsort),基本思想:当插入第i (i 2)个数据时,由于前i-1个数据R1, R2, , Ri-1已排好序,因此可以利用折半查找“在R1, R2, , Ri-1中查找Ri的插入位置”,如此实现的插入排序为折半插入排序。,27,14 36 49 52 80,58 61 23 97 75,i,low,high,m,m,low,low,m,high,14 36 49 52 58 61 80,23 97 75,i,low,high,m,high,m,high,m,low,例如:,再如:,插入 位置,插入 位置,28,折半插入排序的算法
14、如下: void BinaryInsertSort ( ) for (i = 2; i Rmiddle. Key) left = middle + 1;else flag=0; /遇到相同排序码,停止寻找 /end while,29,if (!flag) /flag=0说明序列中有相同排序码的元素left=middle; /从Rmiddle到Ri-1 向后顺移元素for ( k = i-1; k = left; k- ) /元素后移Rk+1 = Rk;Rleft = R0; /end for /BinaryInsertSort,30,由于折半查找比顺序查找快,所以折半插入排序的平均性能比直接插
15、入排序要好。 它所需的排序码比较次数与待排序数据序列的初始排列无关,仅依赖于数据的个数。 在插入第i个数据时,需要经过log2i次排序码比较,才能确定它应插入的位置。 因此,将n个数据用折半插入排序所进行的排序码比较次数为:,折半插入排序的算法分析,31,当n较大时,总的排序码比较次数比直接插入排序的最坏情况要好,但比直接插入排序的最好情况要差。 例如:当数据初始已按排序码排好序或接近有序时,直接插入排序比折半插入排序执行的排序码比较次数要少。,折半插入排序的算法分析,32,折半插入排序虽然能减少排序码的比较次数,但无法减少移动次数,因此该排序算法的时间复杂度仍为O(n2)。(由移动次数决定)
16、 空间效率:只有一个辅助空间R0,即O(1)。 折半插入排序也是一种稳定的排序方法。,折半插入排序的算法分析,讨论:若采用链式存储结构,折半插入排序可行吗?,链表无法“折半”!,33,希尔排序 (Shell Sort),希尔排序方法又称为缩小增量排序。 基本思想: 首先取一个整数dn作为间隔,将全部数据分为d个子序列,所有距离为d的数据放在同一个子序列中(类似于哈希函数的作用),在每一个子序列中分别实施直接插入排序。 然后,缩小间隔d(例如,取d = d/2),重复上述子序列的划分和排序。 直到最后取d=1,将所有数据放在同一个有序序列中为止。,34,其中:d称为增量,它的值在排序过程中从大到
17、小逐渐缩小,直至最后一趟排序减为1。,例如:将n个数据分成d个子序列: R1,R1+d,R1+2d,R1+kd R2,R2+d,R2+2d,R2+kd Rd,R2d,R3d,Rkd,R(k+1)d ,希尔排序 (Shell Sort),35,例:数据序列T=(49,38,65,97, 76, 13, 27, 49*,55, 04),希尔排序的具体过程如下。,分析:开始时增量的值较大,子序列中的数据较少,排序速度较快,可使排序码小的元素很快前移,使得序列基本有序;随着排序进展,增量值逐渐变小,子序列中数据个数逐渐变多,由于前面工作的基础,大多数数据已基本有序,只做局部调整即可,所以排序速度仍然很
18、快。,38,初态:,第1趟 d=5,第2趟 d=3,第3趟 d=1,49,13,13,49,38,27,65,49*,97,55,76,04,27,38,65,49*,97,55,13,55,76,04,55,13,27,04,27,04,49,49*,49,49*,76,38,76,65,65,97,97,13,27,04,49*,76,97,Ri,36,增量的取法有很多种: 最初Shell提出d = n/2,d = gap/2,直到d = 1。 Knuth提出d = d/3 +1。 还有人提出都取奇数为好,也有人提出各增量互质为好。,希尔排序 (Shell Sort),37,希尔排序的算法
19、如下: void Shellsort ( ) d = CurrentSize / 2; /d是间隔while ( d = 1 ) /当间隔d为零时,结束排序ShellInsert(d); /调用一趟直接插入排序d=d/2; /缩小增量d /Shellsort,38,void shellInsert (int d ) /一趟希尔排序,按间隔d划分子序列for ( int i = d; i 0 /shellInsert,39,希尔排序的时间复杂度分析是一个数学上尚未解决的难题。 对特定的待排序数据序列,可以准确地估算排序码的比较次数和数据移动次数。 对一般问题,无法直接给出确切的比较次数和数据移动
20、次数。 希尔排序的时间复杂性介于O(nlog2n)和O(n2)之间,大致为O(n1.3)。 希尔排序是一种不稳定排序方法。,希尔排序 (Shell Sort),40,7.1 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,41,7.3 选择排序,基本思想: 每一趟排序(例如第i趟,i = 1, , n-1)都从剩余的n-i+1个待排数据中选取排序码最小的数据,将其作为有序序列第i个位置的数据。 重复上述过程,直至选出n-1个数据为止(第n个数据不用选择)。,42,7.3 选择排序,基本思想:,
21、43,7.3 选择排序,基本思想: 每一趟排序(例如第i趟,i = 1, , n-1)都从剩余的n-i+1个待排数据中选取排序码最小的数据,将其作为有序序列第i个位置的数据。 重复上述过程,直至选出n-1个数据为止(第n个数据不用选择)。 选择排序包括以下三种具体算法: 直接选择排序 树形选择排序(锦标赛排序) 堆排序,44,基本步骤: 在一个数据表Ri(i=1,n-1)Rn中,选择具有最小排序码的数据; 若它不是这组数据中的第一个数据,则将它与这组数据中的第一个数据对调; 重复执行第、步,直至剩余一个数据为止。,直接选择排序 (Select Sort),45,例:待排序数据表T=(21,25
22、,49,25*,16,08),直接选择排序实现过程如下:,原始序列: 21,25,49,25*,16,08,第1趟 第2趟 第3趟 第4趟 第5趟,08,25,49,25*,16,21 08,16, 49,25*,25,21 08,16, 21,25*,25,49 08,16, 21,25*,25,49 08,16, 21,25*,25,49,直接选择排序 (Select Sort),46,直接选择排序的算法如下: void SelectSort ( ) for (i = 1; i CurrentSize; i+ ) k = i;for (j = i+1; j = CurrentSize; j
23、+) if ( Rj. Key Rk.Key )k = j; /k记当前具有最小排序码的下标if ( k != i ) swap(Ri, Rk); /交换数据 /SelectSort,47,直接选择排序中,排序码的比较次数与数据表的初始排列次序无关。 设整个待排序数据表有n个数据, 则第i趟(i=1, , n-1)选择具有最小排序码的数据所需的比较次数为n-i次。 总的排序码比较次数为:,直接选择排序算法分析:,48,直接选择排序中,数据的移动次数与数据的初始排列有关。 最好情况:当数据表的初始序列有序时,数据的移动次数为0次。 最坏情况:当数据表的初始序列逆序时,总的数据移动次数为3(n-1
24、)次。 经过n-1趟排序,每趟都要移动数据,移动1次数据通常需要引入一个临时变量,并经3次交换(赋值)才能完成。,直接选择排序算法分析:,49,直接选择排序算法的时间复杂度为O(n2)。 在比较次数(n2/2次)和移动次数(3(n-1)次)中选较大者。 直接选择排序算法的空间效率为O(1)。 直接选择排序是一种不稳定的排序方法。,直接选择排序算法分析:,50,树形选择排序(Tree Sort),基本思想:(选择数据的方式如下) 首先,取n个数据的排序码,进行两两比较,得到n/2个优胜者(排序码小者),作为第一步比较的结果保留下来。 然后,对这n/2个数据再进行排序码的两两比较,如此重复,可选出
25、一个排序码最小的数据。 上述过程类似于体育比赛中锦标赛的赛制(如:网球、羽毛球等等)。,51,树形选择排序(Tree Sort),基本思想:(选择数据的方式如下),冠军,52,树形选择排序(Tree Sort),基本思想:(选择数据的方式如下) 首先,取n个数据的排序码,进行两两比较,得到n/2个优胜者(排序码小者),作为第一步比较的结果保留下来。 然后,对这n/2个数据再进行排序码的两两比较,如此重复,可选出一个排序码最小的数据。 上述过程类似于体育比赛中锦标赛的赛制(如:网球、羽毛球等等)。 该过程可用一棵有n个结点的完全二叉树(胜者树)来表示。,如果n不是2的k(k是树的层数)次幂,则让
26、叶结点数目补足到2k-1个。 叶结点上一层的非叶结点是叶结点排序码两两比较的结果。 最顶层是树的根。,08,21,08,08,63,25*,21,21,25,49,25*,16,08,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,数据表T= (21,25,49,25*,16,08,63),54,08,21,08,08,63,25*,21,21,25,49,25*,16,08,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,胜者树,形成初始的胜者树(最小排序码升至根)
27、,树形选择排序(Tree Sort),55,16,21,16,16,63,25*,21,21,25,49,25*,16,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出冠军,调整之后胜者树的状态,树形选择排序(Tree Sort),56,21,21,63,63,25*,21,21,25,49,25*,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出亚军,调整之后胜者树的状态,树形选择排序(Tree Sort),57,25,25,63,63,25*,25,25,
28、49,25*,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出第三名,调整之后胜者树的状态,树形选择排序(Tree Sort),58,25*,25*,63,63,25*,49,25*,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出第四名,调整之后胜者树的状态,树形选择排序(Tree Sort),59,49,49,63,63,49,49,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出第五名,
29、调整之后胜者树的状态,树形选择排序(Tree Sort),60,63,63,63,63,tree7 tree8 tree9 tree10 tree11 tree12 tree13 tree14,输出全部结果时胜者树的状态,树形选择排序(Tree Sort),61,时间效率: 第一趟选择最小排序码的数据需要进行n-1次排序码比较; 选择次小、再次小排序码的数据所需比较次数均为k (k是二叉树的层数)次。 总的排序码比较次数为O(n-1+(n-1)k)= O(nlog2n) 数据的移动次数不会超过排序码的比较次数,因此树形选择排序的时间复杂度为O(nlog2n)。,树形选择排序算法分析:,62,空
30、间效率: 当n=2k时,附加存储单元数目为n-1。 由于满二叉树中只有度为零的结点(n0)和度为2的结点(n2),且n0=n2+1,因此,附加存储单元数目为n-1。 当n2k时,附加存储单元数目为n-1+(2k-1-n)。 除了n-1个度为2的附加结点之外,还有2k-1-n个补齐的叶子结点。 树形排序是一种稳定的排序方法。,树形选择排序算法分析:,63,堆排序是对树形选择排序的改进,它的时间复杂度与树形选择排序相同O(nlog2n)。 堆排序只需一个附加空间,即:空间效率是O(1),比树形选择排序的附加空间要少。,堆排序 (Heap Sort),64,堆的定义:n个元素的排序码序列k1, k2
31、, , kn构成一棵完全二叉树,当且仅当满足如下关系时称该完全二叉树为堆。其中: i=1, 2, , n/2。,堆排序 (Heap Sort),65,例:排序码序列T1=(08, 25, 49, 46, 58, 67)和 T2=(91, 85, 76, 66, 58, 55, 67)分别构建如下完全二叉树。判断它们是否为 “堆”?,大顶堆,小顶堆,66,堆排序的基本思想: (1)建立大顶(或小顶)堆; (2)输出堆顶元素,然后重建大顶(或小顶)堆; (3)重复步骤(2),直至输出所有元素,完成堆排序。 堆排序需要解决两个问题: (1)建堆:如何将一个无序序列建成一个大顶(或小顶)堆; (2)调
32、整堆:如何在输出堆顶元素之后,把剩余元素调整成为一个新堆。,堆排序 (Heap Sort),67,(1)如何建堆,步骤:从最后一个分支结点开始往前逐步向调整,让每个双亲小于或等于(大于或等于)它的左右子女,直到根结点为止。,注:叶结点没有子女,无需单独调整。,68,自下向上逐步调整为最小堆,例:将一组排序码序列53, 17, 78, 09, 45, 65, 87, 23建立小顶堆。,=4,=3,69,例: 将一组序列 53,17,78,09,45,65,87,23 建立最小堆。,=2,=2,70,1,1,例: 将一组序列 53,17,78,09,45,65,87,23 建立最小堆。,71,1,
33、1,例: 将一组序列 53,17,78,09,45,65,87,23 建立最小堆。,72,例: 将一组序列 38,52,45,64,38*,18 建立最大堆。,73,(2)调整堆 在堆排序过程中,主要的工作是调整堆。假设heap1,heapn是初始最大堆,heap1是排序码最大数据元素。将heap1与heapn 的元素进行交换,再对heap1,heapn-1进行调整,使之成为最大堆,依此重复n-1次。,74,75,最大堆的向下调整算法如下: void MaxHeap_HeapAdjust (int i, int EndOfHeap) / i为调整堆的开始位置,EndOfHeap为调整堆的结束位
34、置current = i; child = 2*i;heap0 = heapi; / heap0用作临时存储单元,76,while ( child = heapchild.Key) break; else heapcurrent = heapchild;current = child;child = 2*child; heapcurrent = heap0; / MaxHeap_HeapAdjust,77,堆排序的算法: void MaxHeap_HeapSort () /对表heap1到heapn进行排序, 使得表中各个元素按其排序码非递减有序。for (i = n/2;i0;i- )Max
35、Heap_HeapAdjust(i,n); /建立最大堆for(i=n;i1;i- ) swap(heap1,heapi); /交换MaxHeap_HeapAdjust(1,i-1 ); /重建大顶堆 /MaxHeap_HeapSort,78,堆排序算法分析: 堆排序的时间复杂度为O(nlog2n)。 该算法的附加存储是用heap0作为临时存储单元,所以空间复杂度为O(1)。 堆排序也是一个不稳定的排序方法。,79,建立初始的最大堆,21,25,25*,49,16,08,0,1,2,3,4,5,21 25 49 25* 16 08,初始排序码集合,80,81,82,83,3,84,85,7.1
36、 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,86,交换排序 ( Exchange Sort ),基本思想是两两比较待排序对象的排序码,如果发生逆序(即排列顺序与排序后的次序正好相反),则交换之。直到所有对象都排好序为止。,87,起泡排序 (Bubble Sort),基本方法是:设待排序对象序列中的对象个数为 n。最多作 n-1 趟,i = 1, 2, , n-1 。在第 i 趟中从后向前,j = n-1, n-2, , i,顺次两两比较Vj-1.key和Vj.key。如果发生逆序,则交换
37、Vj-1和Vj。 优点:每趟结束时,不仅能冒出一个最小值 (最大值)到最前(后)面位置,还能同时部分理顺其他元素;一旦下趟没有交换发生,还可以提前结束排序。,88,例如:,2,5,5,3,1,5,7,9,8,9,i=6,1,3,i=5,89,第i趟对待排序对象序列Vi-1,Vi,Vn-1进行排序, 结果将该序列中排序码最小(大)的对象交换到序列的第一个位置(i-1), 其它对象也都向排序的最终位置移动。在个别情形, 对象可能在排序中途向相反的方向移动。 最多做n-1趟起泡就能把所有对象排好序。 起泡排序是一个稳定的排序方法。,90,在对象的初始排列已经按排序码从小到大排好序时,此算法只执行一趟
38、起泡,做n-1次排序码比较,不移动对象。这是最好的情形。 最坏的情形是算法执行n-1趟起泡,第i趟 (1 i n) 做 n- i 次排序码比较, 执行 n-i 次对象交换。这样在最坏情形下总的排序码比较次数KCN和对象移动次数RMN为:,91,快速排序 (Quick Sort),基本思想是任取待排序对象序列中的某个对象 (例如取第一个对象) 作为基准, 按照该对象的排序码大小, 将整个对象序列划分为左右两个子序列: 左侧子序列中所有对象的排序码都小于或等于基准对象的排序码 。 右侧子序列中所有对象的排序码都大于基准对象的排序码。,92,然后分别对这两个子序列重复施行上述方法,直到每个子序列的元
39、素只剩一个。此时便为有序序列了。 优点:因为每趟可以确定不止一个元素的位置,而且呈指数增加,所以特别快!,93,例:关键字序列 T=(21,25,49,25*,16,08),快速排序的算法步骤:,( ),21, 25, 49, 25*,16, 08,初态: 第1趟: 第2趟: 第3趟:,08, 16, 21,25* , 25,(49),21,08,16,,( ),25* ,25,49,08, (16),21,,25* ,(25,49),94,算法quicksort是一个递归的算法, 其递归树如图所示。,那么:一趟排序过程如何实现?,95,例:快速排序算法的一趟实现过程:,0 1 2 3 4 5
40、,96,算法分析,从快速排序算法的递归树可知, 快速排序的趟数取决于递归树的高度。 如果每次划分对一个对象定位后, 该对象的左侧子序列与右侧子序列的长度相同, 则下 一步将是对两个长度减半的子序列进行排序, 这是最理想的情况。,97,在 n个元素的序列中, 对一个对象定位所需时间为 O(n)。若设 T (n) 是对 n 个元素的序列进行排序所需的时间, 而且每次对一个对象正确定位后, 正好把序列划分为长度相等的两个子序列, 此时, 总的计算时间为:T(n) cn + 2T(n/2 ) / c 是一个常数 cn + 2 ( cn/2 + 2T(n/4) ) = 2cn + 4T(n/4) 2cn
41、 + 4 ( cn/4 +2T(n/8) ) = 3cn + 8T(n/8) cn log2n + nT(1) = O(n log2n ),98,快速排序是递归的,需要有一个栈存放每层递归调用时的指针和参数。 最大递归调用层次数与递归树的高度一致,理想情况为 log2(n+1) 。因此,要求存储开销为 O(log2n)。,99,在最坏的情况, 即待排序对象序列已经按其排序码从小到大排好序的情况下, 其递归树成为单支树, 每次划分只得到一个比上一次少一个对象的子序列。必须经过n-1 趟才能把所有对象定位, 而且第 i 趟需要经过 n-i 次排序码比较才能找到第 i 个对象的安放位置,总的排序码比
42、较次数将达到,快速排序退化,100,用第一个对象作为基准对象,快速排序退化的例子,08 16 21 25 25* 49,08,0 1 2 3 4 5 pivot,初始,16 21 25 25* 49,08,16,21 25 25* 49,21,08 16,25,25 25* 49,08 16 21,25* 49,25*,08 16 21 25,49,08 16 21 25 25*,i = 1,i = 2,i = 3,i = 4,i = 5,101,其排序速度退化到简单排序的水平, 比直接插入排序还慢。占用附加存储(栈)将达到O(n)。 改进办法: 取每个待排序对象序列的第一个对象、最后一个对象
43、和位置接近正中的 3 个对象,取其排序码居中者作为基准对象。,102,快速排序是一种不稳定的排序方法。 对于 n 较大的平均情况而言, 快速排序是“快速”的, 但是当 n 很小时, 这种排序方法往往比其它简单排序方法还要慢。,103,7.1 内部排序方式 7.2 插入排序 7.3 选择排序 7.4 交换排序 7.5 归并排序 7.6 基数排序 7.7 各种内部排序算法的比较,第7章 排序,104,归并排序 (Merge Sort),归并,是将两个或两个以上的有序表合并成一个新的有序表。 例:对象序列initList中两个有序表Vl Vm和Vm+1 Vn。它们可归并成一个有序表, 存于另一对象序
44、列mergedList的Vl Vn中。 这种归并方法称为两路归并(2-way merging)。 设变量 i 和 j 分别是表Vl Vm和Vm+1 Vn的当前检测指针。变量 k 是存放指针。,105,08 16 21 25 25* 37 49 54 62 72 93,left right,k,mergeList,当 i 和 j 都在两个表的表长内变化时, 根据对应项的排序码的大小, 依次把排序码小的对象排放到新表 k 所指位置中; 当 i 与 j 中有一个已经超出表长时,将另一 个表中的剩余部分照抄到新表中。,106,迭代的归并排序算法,迭代的归并排序算法就是利用两路归并过程进行排序的。其基本
45、思想是: 假设初始对象序列有 n 个对象,首先把它看成是 n 个长度为 1 的有序子序列 (归并项),先做两两归并,得到 n / 2 个长度为 2 的归并项 (如果 n 为奇数,则最后一个有序子序列的长度为1);再做两两归并,如此重复,最后得到一个长度为 n 的有序序列。,107,迭代的归并排序算法,len=2,len=4,len=8,len=16,108,在迭代的归并排序算法中, 函数MergePass( ) 做一趟两路归并排序, 要调用merge ( )函数 n/(2*len) O(n/len) 次, 函数MergeSort( )调用MergePass( )正好log2n 次,而每次mer
46、ge( )要执行比较O(len)次, 所以算法总的时间复杂度为O(nlog2n)。 归并排序占用附加存储较多, 需要另外一个与原待排序对象数组同样大小的辅助数组。这是这个算法的缺点。 归并排序是一个稳定的排序方法。,109,递归的表归并排序,与快速排序类似,归并排序也可以利用划分为子序列的方法递归实现。 在递归的归并排序方法中,首先要把整个待排序序列划分为两个长度大致相等的部分,分别称之为左子表和右子表。对这些子表分别递归地进行排序,然后再把排好序的两个子表进行归并。 图示:待排序对象序列的排序码为 21, 25, 49, 25*,16, 08 ,先是进行子表划分,待到子表中只有一个对象时递归到底。再是实施归并,逐步退出递归调用的过程。,110,21 25 49 25* 16 08,21 25 49,25* 16 08,21,25,49,25 49,21,25*,16 08,25*,16,08,