计算机数据结构第十章 排序_第1页
计算机数据结构第十章 排序_第2页
计算机数据结构第十章 排序_第3页
计算机数据结构第十章 排序_第4页
计算机数据结构第十章 排序_第5页
已阅读5页,还剩91页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十章排序什么是排序?

排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。例如:将下列关键字序列52,49,80,36,14,58,61,23,97,75调整为14,23,36,49,52,58,61,75,80,9710.1排序一般情况下,假设含n个记录的序列为{R1,R2,…,Rn}其相应的关键字序列为{K1,K2,…,Kn}这些关键字相互之间可以进行比较,即在它们之间存在着这样一个关系:

Kp1≤Kp2≤…≤Kpn按此固有关系将上式记录序列重新排列为

{Rp1,Rp2,…,Rpn}的操作称作排序。稳定的排序方法若ki=kj,且排序前的序列中Ri领先于Rj,排序后Ri仍领先于Rj,则称所用的排序方法是稳定的;反之,若可能使排序后的序列中Rj仍领先于Ri

,则称所用的排序方法是不稳定的。内部排序和外部排序待排序的记录存放再计算机的内存中所进行的排序操作称为内部排序。待排序的记录数量很大,以致内存一次不能容纳全部记录,在排序过程中需要访问外存的排序过程称为外部排序。正序与逆序若有序表是按排序码升序排列的,则称为升序表或正序表,否则称为降序表或逆序表。不失普遍性,我们一般只讨论正序表。排序方法度量排序过程主要是进行记录的关键字比较和记录的移动。因此排序的时间复杂性可以算法执行中的数据比较次数及数据移动次数来衡量。当一种排序方法使排序过程在最坏或平均情况下所进行的比较和移动次数越少,则认为该方法的时间复杂性就越好。针对一种排序方法,不仅要分析它的时间复杂性,而且要分析它的空间复杂性、稳定性和简单性等。常见排序的方法内部排序外部排序插入排序(直插排序、二分排序、希尔排序)交换排序(冒泡排序、快速排序)选择排序(直选排序、树型排序、堆排序)归并排序(二路归并排序、多路归并排序)分配排序(多关键字排序、基数排序)多路平衡归并排序置换-选择排序最佳归并树排序10.2插入排序直接插入排序原理直接插入排序(StraightInsertionSorting)的基本思想是:把n个待排序的元素看作由两部分组成:一个有序表和一个无序表。开始时有序表中只包含一个元素,无序表中包含有n-1个元素。排序过程中,每次从无序表中取出第一个元素,令其关键字依次与有序表元素的关键字进行比较,将其插入到有序表中的适当位置,使有序表的长度不断加长,完成排序过程。直接插入排序过程示例初始关键字序列3412492831525149*123456780监视哨i=21234492831525149*12i=31234492831525149*49i=41228344931525149*28i=51228313449525149*31i=61228313449525149*52i=71228313449515249*51i=8122831344949*515249*直接插入排序算法数据结构定义#defineMAXSIZE20typedefintKeytype;//定义关键字类型为整型typedefstruct{KeyTypekey;//关键字项InfoTypeotherinfo;//其他数据项}RedType;//记录类型typedefstruct{RedTyper[MAXSIZE+1];//r[0]闲置或用作哨兵intlength;//顺序表长度}SqList;//顺序表类型以顺序表作为存储结构的直接插入排序算法voidInsertSort(SqList&L){

for(i=2;i<=L.ength;i++)if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//if}//InsertSort分析直接插入排序算法中关键字的比较次数和记录移动次数for(i=2;i<=L.ength;i++)

if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//ifif(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key;j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//if最好情况下(正序)元素的比较次数为:n-1元素的移动次数为:0最坏情况下(逆序)元素的比较次数:(n+2)(n-1)/2元素的移动次数为:(n+4)(n-1)/2直接插入排序算法时间复杂度以顺序表作为存储结构的直接插入排序算法时间复杂度:O(n2)在最好情况下(正序),元素的移动次数为0,比较次数为n–1;在最坏情况下(逆序),元素的移动次数为(n+4)(n-1)/2,比较次数为(n+2)(n-1)/2空间复杂度:O(1)只需要1个辅助单元稳定的排序方法适用情况元素数目少,或者元素的初始序列基本有序10.2.2.其他插入排序在寻找插入位置时采用二分查找,则称为折半插入排序,2-路插入排序在此基础上增加了辅助空间、减少了记录的移动。10.2.3希尔排序希尔排序(Shell’sSort)也称为缩小增量排序,其改进原理主要基于以下两点元素基本有序时,直接插入排序的时间复杂度接近于O(n)元素数目n较少时,直接插入排序的效率较高希尔排序的算法思想先将整个待排序元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的),分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。10.2希尔排序过程示例初始关键字序列:4938659776132749*123456785504910增量d=5132749*5504493865123456789776910第一趟排序结果:增量d=3第二趟排序结果:130449*3827495565123456789776910增量d=1第三趟排序结果:0413273849*495565123456787697910132749*55044938659776希尔排序算法voidShellInsert(SqList&L,intdk){//对顺序表L作一趟希尔排序

for(i=2;i<=L.ength;i++)if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//if}//ShellInsertSortfor(i=dk+1i<=L.ength;i++)

if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//ifif(L.r[i].key<L.r[i-dk].key){//需将L.r[i]插入有序增量子表L.r[0]=L.r[i];//L.r[i]暂存入L.r[0]for(j=i-dk;j>0&&L.r[0].key<L.r[j].key;j-=dk)L.r[j+dk]=L.r[j];//寻找插入位置时记录后移L.r[j+dk]=L.r[0];//插入}//ifvoidShellInsert(SqList&L,intdk){//对顺序表L作一趟希尔排序L......}//ShellInsertSortvoidShellSort(SqList&L,intdlta[],intt){//按增量序列dlta[0..t-1]进行希尔排序for(k=0;k<t;k++)ShellInsert(L,dlta[k]);//一趟增量为dlta[k]的希尔排序}//ShellInsertSort希尔排序的分析是一个复杂问题,增量序列的设置是关键,尚没有正式的依据说明如何设置最佳的增量序列,大量的实验表明希尔排序所需的比较和移动次数可达到n1.3希尔排序的空间复杂度为O(1),是不稳定的排序方法10.3交换排序10.3.1起泡排序起泡排序(BubbleSorting)的基本思想是:将相邻位置的关键字进行比较,若为逆序则交换之。第i趟排序过程为从L.r[1]至L.r[n-i+1]依次比较相邻两个记录的关键字,并在“逆序”时交换相邻记录,其结果是这n-i+1个记录中关键字最大的记录被交换到第n-i+1的位置上。整个排序过程终止的条件是“在一趟排序过程中没有进行过交换记录的操作”起泡排序过程示例初始关键字序列:4938659776132749*1234567838496576132749*97第一趟排序后:384965132749*7697第二趟排序后:3849132749*657697第三趟排序后:3813274949*657697第四趟排序后:1327384949*657697第五趟排序后:1327384949*657697第六趟排序后:起泡排序算法以顺序表作为存储结构的起泡排序算法voidBubbleSort(SqList&L){

for(i=2;i<=L.ength;i++)if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];}//if}//BubbleSort分析起泡排序算法中关键字的比较次数和记录移动次数for(i=1,change=TRUE;i<L.length&&change;++i){

if(L.r[i].key<L.r[i-1].key){L.r[0]=L.r[i];L.r[i]=L.[i-1];for(j=i-2;L.r[0].key<L.r[j].key];j--)L.r[j+1]=L.r[j];L.r[j+1]=L.r[0];

}//for}//if

change=FALSE;for(j=1;j<L.length-i+1;++j)if(L.r[j].key>L.r[j+1].key){L.r[0]=L.r[j];L.r[j]=L.r[j+1];L.r[j+1]=L.r[0];change=TRUE;}//if最好情况下,元素的比较次数为:n-1最坏情况下,交换次数为:n(n-1)/2最好情况下,元素的移动次数为:0最坏情况下,交换次数为:n(n-1)/2起泡排序算法分析以顺序表作为存储结构的起泡排序算法时间复杂度:O(n2)在最好情况下(正序),元素的交换次数为0,比较次数为n–1;在最坏情况下(逆序),元素的交换次数为n(n-1)/2,比较次数为(n+2)(n-1)/2空间复杂度:O(1)只需要1个辅助单元进行交换稳定的排序方法适用情况元素数目少,或者元素的初始序列基本有序10.3.2.快速排序快速排序(QuickSorting)是迄今为止所有内排序算法中速度最快的一种。它的基本思想是:任取待排序序列中的某个元素作为基准(一般取第一个元素),通过一趟排序,将待排元素分为左右两个子序列,左子序列元素的关键字均小于或等于基准元素的关键字,右子序列的关键字则大于基准元素的关键字,然后分别对两个子序列继续进行排序,直至整个序列有序。386597132749*551234567849049pivotij快速排序中的一趟划分快速排序中的一趟划分386597132749*55123456784904949ijL.r[j]与pivot比较,L.r[j]小则与L.r[i]交换;否则j减10449L.r[j]与L.r[i]交换,i加1快速排序中的一趟划分386597132749*55123456780449949pivotijL.r[i]与pivot比较,L.r[i]大则与L.r[j]交换;否则i增1快速排序中的一趟划分L.r[i]与pivot比较,L.r[i]大则与L.r[j]交换;否则i增1386597132749*55123456780449949pivotij4965L.r[i]与L.r[j]交换,j减1快速排序中的一趟划分(续)384997132749*55123456780465949ijL.r[j]与pivot比较,L.r[j]小则与L.r[i]交换;否则j减1快速排序中的一趟划分384997132749*55123456780465949pivotijL.r[j]与pivot比较,L.r[j]小则与L.r[i]交换;否则j减1快速排序中的一趟划分L.r[j]与pivot比较,L.r[j]小则与L.r[i]交换;否则j减1384997132749*55123456780465949pivotij2749L.r[j]小则与L.r[i]交换,i加1快速排序中的一趟划分pivot382797134949*55123456780465949pivotijL.r[i]与pivot比较,L.r[i]大则与L.r[j]交换;否则i增1L.r[i]大则与L.r[j]交换,j减14997快速排序中的一趟划分pivotL.r[j]与pivot比较,L.r[j]小则与L.r[i]交换;否则j减1382749139749*55123456780465949pivotijL.r[j]小则与L.r[i]交换,i加11349快速排序中的一趟划分382713499749*55123456780465949pivotij当i与j相等时,基准元素确定了位置i,结束本次划分386597132749*551234567849049pivot快速排序中的一趟划分算法382713499749*551234567804659第趟划分结果第一组第二组intPartition(SqList&L,intlow,inthigh){pivotkey=L.r[low].key;i=low;j=high;while(i<j){

while(i<j&&L.r[j].key>=pivotkey)j--;L.r[i]←→L.r[j];

while(i<j&&L.r[i].key<=pivotkey)i++;L.r[j]←→L.r[i];}returni;}//Partition386597132749*551234567849049划分382713499749*550465划分38271304划分9749*5565划分132738划分2713划分49*5565划分65552765将交换改为元素的移动划分算法改进386597132749*55123456784904949ij0449386597132749*55123456784904949ij04386597132749551234567849049pivotij快速排序中的一趟划分快速排序中的一趟划分386597132749551234567804949pivotija[j]与pivot比较,a[j]小则a[j]→a[i]快速排序中的一趟划分386597132749551234567804949pivotij快速排序中的一趟划分386597132749551234567804949pivotija[i]与pivot比较,a[i]大则a[i]→a[j];否则i增1快速排序中的一趟划分386597132749551234567804949pivotija[i]与pivot比较,a[i]大则a[i]→a[j];否则i增1快速排序中的一趟划分389713274955123456780465949pivotij快速排序中的一趟划分389713274955123456780465949pivotija[j]与pivot比较,a[j]小则a[j]→a[i];否则,利用j向前扫描快速排序中的一趟划分389713274955123456780465949pivotija[j]与pivot比较,a[j]小则a[j]→a[i];否则,利用j向前扫描快速排序中的一趟划分389713274955123456780465949pivotija[j]与pivot比较,a[j]小则a[j]→a[i];否则,利用j向前扫描快速排序中的一趟划分382797134955123456780465949pivotija[i]与pivot比较,a[i]大则a[i]→a[j];否则,利用i向后扫描快速排序中的一趟划分382797134955123456780465949pivotij利用i向后扫描a[i]与pivot比较,a[i]大则a[i]→a[j];快速排序中的一趟划分382713974955123456780465949pivotij利用j向前扫描快速排序中的一趟划分382713974955123456780465949pivotija[j]与pivot比较,a[j]小则a[j]→a[i];否则,利用j向前扫描快速排序中的一趟划分382713974955123456780465949pivotija[i]与pivot比较,a[i]大则a[i]→a[j];否则,利用i向后扫描快速排序中的一趟划分382713974955123456780465949pivotij快速排序中的一趟划分382713974955123456780465949pivotiji与j相等时,完成一次划分快速排序中的一趟划分382713499749551234567804659intPartition(SqList&L,intlow,inthigh){L.r[0]=L.r[low];pivotkey=L.r[0].key;i=low;j=high;while(i<j){

while(i<j&&L.r[j].key>=pivotkey)j--;L.r[i]=L.r[j];

while(i<j&&L.r[i].key<=pivotkey)i++;L.r[j]=L.r[i];}L.r[i]=L.r[0];returni;}//Partition快速排序intPartition(SqList&L,intlow,inthigh){......returni;//返回枢轴元素的位置}//PartitionvoidQSort(SqList&L,intlow,inthigh){//对L.r[low]~L.r[high]的元素进行快速排序if(low<high){pivotloc=Partition(L,low,high);//一趟划分QSort(L,low,pivotloc-1);QSort(L,pivotloc+1,high);}//if}//QSort快速排序过程分析386597132749*551234567849049划分382713499749*550465划分38271304划分9749*5565划分132738划分2713划分49*5565划分65552765以顺序表作为存储结构的快速排序算法时间复杂度:O(nlogn)快速排序在最好情形下(左、右子区间的长度大致相等),则结点数n与二叉树深度h应满足log2n<h<log2n+1,所以总的比较次数不会超过(n+1)log2n。因此,快速排序的最好时间复杂度应为O(nlog2n)。理论上已经证明,快速排序的平均时间复杂度也为O(nlog2n)。在最坏情况下(逆序或正序),时间复杂度为O(n2)空间复杂度:O(logn)在最坏情况下(逆序或正序),空间复杂度为O(n)不稳定的排序方法快速排序不适合对小规模的序列进行排序10.3快速排序方法的改进三者取中的方法选取枢轴元素划分的过程中进行“起泡”操作快速排序算法被认为是内部排序方法中最好的一种1327384949*55650497123456789最坏情况下的快速排序1327384949*556504971327384949*55659727384949*556597384949*5565974949*55659749*556597556597659797最好情况下的快速排序046597135549*381234567849279划分043813495549*972765划分13043827划分6549*5597划分380413划分49*976565划分0410.4选择排序10.4.1简单选择排序简单选择排序的基本思想是:第一趟在n个记录中选取最小记录作为有序序列的第一个记录,第二趟在n-1个记录中选取最小记录作为有序序列的第二个记录,第i趟在n-i+1个记录中选取最小的记录作为有序序列多中的第i个记录。简单选择排序过程示例初始关键字序列3412492831525149*123456780i=11234492831525149*i=21228493431525149*i=31228313449525149*i=41228313449525149*i=51228313449525149*i=6122831344949*5152i=7122831344949*5152return简单选择排序算法以顺序表作为存储结构的简单选择排序算法voidSelectSort(SqList&L){//对顺序表作简单选择排序

for(i=1;i<L.ength;i++){for(k=i,j=i;k<=L.length;k++)if(L.r[k].key<L.r[j].key)j=k;if(j!=i){L.r[i]←

→L.r[j];}}//for}//SelectSort分析简单选择排序算法中关键字的比较次数和记录移动次数for(i=1;i<L.ength;i++){

for(k=i,j=i;k<=L.length;k++)if(L.r[k].key<L.r[j].key)j=k;if(j!=i){L.r[i]←

→L.r[j];}}//for}//iffor(k=i+1,j=i;k<=L.length;k++)if(L.r[k].key<L.r[j].key)j=k;if(j!=i){L.r[i]←

→L.r[j];}在逆序情况下元素的比较次数:n(n-1)/2元素的移动次数为:3(n-1)/2示例在正序情况下元素的比较次数:n(n-1)/2元素的移动次数为:0简单选择排序算法分析以顺序表作为存储结构的简单选择排序算法时间复杂度:O(n2)在n个关键字中选出最小者,需要n-1次比较,继续在剩余的n-1个元素中选出次小者需要n-2次比较,依此类推。空间复杂度:O(1)只需要1个辅助单元进行交换不稳定的排序方法适用情况元素数目少的情况无需完全排序的情况,比如,选出第i小的元素对简单选择排序过程进行改进:利用前面已经进行过的比较结果10.4.2树形选择排序(TreeSelectionSort)又称锦标赛排序(TournamentSort):首先对n个记录的关键字两两进行比较,然后在n/2个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录。整个过程可用一个含有n个叶结点的二叉树表示。例如3412492831525149*12283149*1231123412

492831525149*2.树形选择排序(TreeSelectionSort)又称锦标赛排序(TournamentSort):首先对n个记录的关键字两两进行比较,然后在n/2个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录。选出最小记录后,将树中的该最小记录修改为∞,然后从该叶子结点所在子树开始,修改到达树根的路径上的结点34∞

492831525149*34283149*28312834∞

492831525149*2.树形选择排序(TreeSelectionSort)又称锦标赛排序(TournamentSort):首先对n个记录的关键字两两进行比较,然后在n/2个较小者之间再进行两两比较,如此重复,直至选出最小关键字的记录。选出最小记录后,将树中的该最小记录修改为∞,然后从该叶子结点所在子树开始修改到达树根的路径上的结点以后每选出一个小元素,都只需进行(logn)次比较34∞

49∞31525149*34493149*34313134∞

492831525149*树形选择排序的缺陷需要较多的辅助空间存在与“∞”进行比较的冗余比较34∞

49∞31525149*34493149*34313110.4.3堆排序(HeapSort)只需要一个元素的辅助空间算法的时间复杂度为O(nlogn)堆的定义对于n个元素的序列{k1,k2,...,kn},当且仅当满足以下关系时,称之为堆。

或堆(完全二叉树)96833811279(a)大顶堆(maxheap)123685472430(b)小顶堆(minheap)5391堆排序(HeapSort)原理对一组待排序记录的关键字,首先把它们按堆的定义建成小(大)顶堆,然后输出堆顶的最小(大)关键字所代表的记录,再对剩余的关键字建堆,以便得到次小(大)的关键字,如此反复进行,直到全部关键字排成有序序列为止。实现堆排序需要解决两个问题:

(1)如何建堆?

(2)输出堆顶元素后,如何将剩余元素重新调整为一个新堆?堆排序过程示例下面是一个小顶堆,输出堆顶元素后,将剩余元素重新调整为一个新堆的方法是:从树根开始,若以某结点为根的子树不是堆,则将其孩子中的较小者与之交换,即将非堆的子树推向叶子方向12368547243053919136854724305312交换堆顶元素与序列末端的元素比较比较-交换输出堆顶元素后,将剩余元素重新调整为一个新堆的方法是:从树根开始,若以某结点为根的子树不是堆,则将其孩子中的较小者与之交换,即将非堆的子树推向叶子方向2436854730915312继续向叶子方向调整2436854791305312比较比较交换堆排序过程示例一旦已调整为堆,则输出堆顶元素,重复将剩余元素重新调整为一个新堆。24368547309153125336854730912412交换堆顶元素与序列末端的元素堆排序过程示例输出堆顶元素后,将剩余元素重新调整为一个新堆3036854753912412调整成堆5336854730912412堆排序过程示例输出堆顶元素后,将剩余元素重新调整为一个新堆9136854753302412反复将堆顶元素与序列末端的元素的交换,并重新调整成堆。9185473653302412调整堆元素(筛选)

对于给出的关键字序列,经初始建堆后便得到小(大)顶堆,从而得到最小(大)关键字。

在输出堆顶元素之后,用堆中最后一个元素替代之。此时由于以K2和K3为根的子树仍具有堆的特性,因此只需自上而下进行调整即可。调整过程为:首先令K1的两个子树根进行比较,令其中较小(大)者与K1相比较,将最小(大)元素交换至K1,使得K1、K2和K3成为堆。由于交换后破坏了子树的堆性质,则需再次进行与上述过程类似的调整,直至进行到最下层的叶结点为止。调整后即得到一个有n-1个元素的新堆,从而得到次小关键字。堆排序过程示例输出堆顶元素后,将剩余元素重新调整为一个新堆调整堆元素(筛选)

对于给出的关键字序列,经初始建堆后便得到小(大)顶堆,从而得到最小(大)关键字。在输出堆顶元素之后,用堆中最后一个元素替代之。此时由于以K2和K3为根的子树仍具有堆的特性,因此只需自上而下进行调整即可。首先令K1将的两个子树根进行比较,令其中较小(大)者与K1相比较,将最小(大)元素交换至K1,使得K1、K2和K3成为堆。由于交换后破坏了右子树的堆,则需再次进行与上述类似的调整,直至进行到最下层的叶结点。调整后即得到一个有n-1个元素的新堆,从而得到次小关键字。重复上述过程,将堆顶元素与堆中最后一个元素交换且调整,又得到了有n-2个元素的新堆。为节省存贮开销,可以把输出的最小(大)关键字放在Kn的位置上,把第二次输出的次小(大)关键字存放在Kn-1的位置上……,直至最大(小)关键字放在K1的位置上。如此,我们已经可以在初始情况为堆的情况下完成排序,那么,如何建立起初始堆呢?堆排序过程示例建初始堆47369112533024851236854724305391建初始堆4736911253302485(a)4736851253302491(b)从最后一个具有叶子的结点(编号[n/2])开始建子堆,依次考查结点[n/2]-1,[n/2]-2,...,1等是否为堆,若否则调整为堆return建初始堆4736851224305391从最后一个具有叶子的结点(编号[n/2])开始建子堆,依次考查结点[n/2]-1,[n/2]-2,...,1等是否为堆,若否则调整为堆(C)4712853624305391(d)建初始堆1247853624305391从最后一个具有叶子的结点(编号[n/2])开始建子堆,依次考查结点[n/2]-1,[n/2]-2,...,1等是否为堆,若否则调整为堆(e)1236854724305391(f)当以下标1为根的完全二叉树为堆时,初始堆已建立也可以从空堆开始建初始堆10.4堆排序1964年弗洛伊德(Floyd)提出了筛选法建立初始堆,具体方法是:将待排序的关键字分放到一棵完全二叉树的各个结点中(此时完全二叉树并不一定具备堆的特性),显然,所有i≥[n/2]的结点Ki都没有子结点,以这样的Ki为根的子树已经是堆,因此初始建堆可从完全二叉树的第i个结点Ki开始(i=[n/2])。通过调整,逐步使以K[n/2],K[n/2]-1,K[n/2]-2,…为根的子树满足堆的定义,直至进行到以K1为根的树排成堆为止。在对Ki为根的子树建堆时,其子树已经为堆,要使得以Ki为根的完全二叉树为堆,则可能要调整父、子结点的位置,如此下一层已建成堆的子树可能不再满足堆的定义,则需继续进行调整,如此一次次递推下去,最多可能一直延伸到树叶。这种方法就像过筛子一样,把最小(大)的关键字一层一层地筛选出来,最后输出堆顶的最小(大)关键字。10.4堆排序算法(筛选算法)typedef

SqList

HeapType;//堆采用顺序存储结构voidHeapAdjust(HeapType&H,ints,intm){}//HeapAdjustrc=H.r[s];

for(j=2*s;j<=m;j*=2){//沿key较大的孩子结点向下筛选

if(j<m&<(H.r[j].key,H.r[j+l].key)++j;

//j为key较大的记录的下标

if(!LT(rc.key,H.r[j].key))break;

H.r[s]=H.r[j];//较大的孩子结点值换到父结点位置s=j;}H.r[s]=rc;//rc应插入的位置在s处//H.r[s..m]中除H.r[s].key外均满足堆的定义//调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆示例10.4堆排序算法(续)typedef

SqList

HeapType;//堆采用顺序存储结构voidHeapSort(HeapType&H){//对顺序表H进行堆排序

for(i=H.length/2;i>0;--i)//把H建成大顶堆HeapAdjust(H,i,H.length);for(i=H.length;i>1;--i){H.r[1]←→H.r[i];//堆顶记录和当前未排子序列中最后一个记录相交换HeapAdjust(H,1,i-1);//将H.r[l..i-1]重新调整为大顶堆}}//HeapSort

for(i=H.length/2;i>0;--i)//把H建成大顶堆HeapAdjust(H,i,H.length);

for(i=H.length;i>1;--i){H.r[1]←→H.r[i];//堆顶记录和当前未排子序列中最后一个记录相交换HeapAdjust(H,1,i-1);//将H.r[l..i-1]重新调整为大顶堆}示例10.4堆排序算法分析由于是完全二叉树,所以采用顺序存储结构时间复杂度:O(nlog2n)堆排序的整个算法时间是由建立初始堆和不断调整堆这两部分时间代价构成的。空间复杂度:O(1)只需要1个辅助单元进行交换不稳定的排序方法对于记录数较少的序列来说,堆排序的优越性并不明显,但对大量的记录来说堆排序是很有效的。筛选过程示例下面是一个大顶堆,输出堆顶元素后,将剩余元素重新调整为一个新堆的方法是:从树根开始,若以某结点为根的子树不是堆,则将其孩子中的较大者与之交换,即将非堆的子树推向叶子方向9153364785302412交换堆顶元素与序列末端的元素比较比较-交换return(a)大顶堆1253364785302491(b)8553364712302491(c)8553364730122491(d)大顶堆比较比较交换筛选过程示例下面是一个大顶堆,输出堆顶元素后,将剩余元素重新调整为一个新堆的方法是:从树根开始,若以某结点为根的子树不是堆,则将其孩子中的较大者与之交换,即将非堆的子树推向叶子方向9153364785302412交换堆顶元素与序列末端的元素比较比较-交换return(a)小顶堆1253364785302491(b)8553364712302412(c)小顶堆交换堆顶元素与序列末端的元素5336854791302412(d)10.5归并排序归并所谓“归并”,是将两个或两个以上的有序序列合并成为一个新的有序序列。从第二章线性表的讨论中得知,将两个有序表归并为一个有序表,无论是顺序存储结构还是链式存储结构,都是容易实现的。利用归并的思想可以进行排序。归并排序归并排序是把一个n个记录的无序序列看成是由n个长度为1的有序子序列组成的序列,然后进行两两归并,得到[n/2]个长度为2或1的有序子序列,再两两归并,……,如此重复,直到最后形成包含n个记录的一个有序序列为止。这种总是反复将两个有序序列归并成一个有序序列的排序方法称为两路归并排序。10.5两路归并过程示例设初始关键字序列为:4834608075122648*[48][34][6O][80][75][12][26][48*]第一趟归并:[34,48,60,80]第三趟归并:[12,26,34,48,48*,60,75,80][60,80][12,75][26,48*][12,26,48*,75][34,48]第二趟归并:10.5先分解再归并设初始关键字序列为:[4834608075122648*][48346O8075122648*][75122648*][48346080][4834][6080][7512][2648*][48][34][60][80][75][12][26][48*][3448][6080][1275][2648*][122648*75][34486080][1226344848*607580]10.5两路归并排序算法递归算法voidMergeSort(待排序列){//归并排序if(待排序列长度>1){MergeSort(待排序列的前半区间);MergeSort(待排序列的后半区间);已排好序的前半区间和已排好序的后半区间合并成一个有序序列;}//if}//MergeSort采用顺序存储结构,对于由下标s~t界定的一个序列,前半区间为s~(s+t)/2,后半区间为(s+t)/2+1~tvoidMergeSort(SqList&L,ints,intt){//归并排序if(s<t){m=(s+t)/2;MergeSort(L,s,m);MergeSort(L,m+1,t);Merge(L,s,m,t);//合并L.r[s]~L.r[m]与L.r[m+1]~L.r[t]}//if}//MergeSort10.5两路归并算法以顺序表作为存储结构voidMerge(SqList&L,inti,intm,intn){//两路归并//引入辅助空间temp}//Mergeb=i;for(j=m+1,k=1;i<=m&&j<=n;++k){

}//for//ifi]if(L.r[i].key<L.r[j].key)temp.r[k]=L.r[i++];elsetemp.r[k]=L.r[j++];for(;i<=m;)temp.r[k++]=L.r[i++];for(;

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论