6 内部排序.docx
- 文档编号:7502408
- 上传时间:2023-01-24
- 格式:DOCX
- 页数:21
- 大小:134.18KB
6 内部排序.docx
《6 内部排序.docx》由会员分享,可在线阅读,更多相关《6 内部排序.docx(21页珍藏版)》请在冰豆网上搜索。
6内部排序
第6讲内部排序
本章主要考核内容如下:
排序的基本概念,直接插入排序,折半插入排序,起泡排序,简单选择排序,希尔排序,快速排序,堆排序,二路归并排序,基数排序,各种内部排序算法的比较,内部排序算法的应用等。
知识点分析
(一)排序的基本概念
1.排序的定义
1)排序:
把一组无序地数据元素按照关键字值递增(或递减)地重新排列。
如果排序依据的是主关键字,排序的结果将是唯一的;如果排序依据是次关键字,则排序的结构可能不唯一。
2)稳定排序和不稳定排序:
如果在待排序的记录序列中有多个数据元素的关键字值相同,经过排序后,这些数据元素的相对次序保持不变,则称这种排序算法是稳定的,否则称之为不稳定的。
这往往发生在利用次关键字排序的时候。
如果按照次关键字排序,由于排序方法的不同,可能出现“对次关键字相同的两个元素,原来排在前面的元素,排序后排在了后面”。
即原来顺序是“49,49”排序后变为“49,49”,这种排序方法称为“不稳定排序”,否则称为“稳定排序”。
3)内部排序和外部排序:
根据在排序过程中待排序的所有数据元素是否全部被放置在内存中,可将排序方法分为内部排序和外部排序两大类。
内部排序是指在排序的整个过程中,待排序的所有数据元素全部被放置在内存中;外部排序是指由于待排序的数据元素个数太多,不能同时放置在内存,而需要将一部分数据元素放置在内存,另一部分数据元素放置在外存中,整个排序过程需要在内外存之间多次交换数据才能得到排序的结果。
4)排序过程的两项基本工作
①比较两个关键字的大小;
②将记录从一个存储位置移动到另一个存储位置。
(有时可以用某种方法避免该步骤)
说明:
1>任何排序方法都需要以上过程;
2>记录移动位置时,移动到的位置可能是最终位置,也可能不是,视不同的方法而定。
2.排序的分类
根据排序原则可将排序方法分为以下几类:
①插入排序:
将一个记录插入到一个已经排好序的有序表中,使得新表仍然有序。
②交换排序:
比较两个元素,如果不有序,则交换位置,每趟比较都会有某个记录存放在恰当位置。
③选择排序:
每一次从剩余元素中选择出一个最小(或最大)的,放在确定的位置。
④归并排序:
将两个或多个有序表组合成一个新的有序表。
⑤基数排序:
是一种多关键字排序。
3.排序算法的效率
评价排序算法的效率主要有两点:
一是在数据量规模一定的条件下,算法执行所消耗的平均时间,对于排序操作,时间主要消耗在关键字之间的比较和数据元素的移动上,因此我们认为,高效率的排序算法应该是尽可能少的比较次数和尽可能少的数据元素移动次数;二是执行算法所需要的辅助存储空间,辅助存储空间是指在数据量规模一定的条件下,除了存放待排序数据元素占用的存储空间之外,执行算法所需要的其他存储空间,理想的空间效率是算法执行期间所需要的辅助空间与待排序的数据量无关。
4.待排序序列的参考存储结构
待排序记录序列可以用顺序存储结构和链式存储结构表示。
我们将待排序的记录序列用顺序存储结构表示,即用一维数组实现。
大部分算法,都可按照以下定义的数据类型设定。
#defineMAXSIZE20
typedefintKeyType;//定义关键字类型为整数类型(也可以为其它类型)
typedefstruct{
KeyTypekey;//关键字项
InfoTypeotherinfo;//其它数据项
}RecordType;
typedefstruct{
RecordTyper[MAXSIZE+1];//r[0]闲置或者作为哨兵单元
intlength;//顺序表长度
}SqList;//顺序表类型
『真题解析』
1.某内排序方法的稳定性是指()。
A.该排序算法不允许有相同的关键字记录
B.该排序算法允许有相同的关键字记录
C.平均时间为0(nlogn)的排序方法
D.以上都不对
【答案】D。
【解析】待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,则该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。
注意:
排序算法的稳定性是针对所有输入实例而言的。
即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。
(二)插入排序
1.直接插入排序
直接插入排序是一种最基本的排序算法,基本操作为:
将一个记录插入到一个已经排好序的有序表中,从而得到一个新的、长度增1的有序表。
一般情况下,第i趟的操作为:
在含有i-1个记录的有序子序列r[1..i-1]中插入一个新记录r[i],变成含有i个记录的有序序列r[1..i]。
设置r[0]为空值,从r[1]开始保存信息,可首先将待插入的记录r[i]复制到r[0]中,如下所示:
r[0]
r[1]
r[2]
r[3]
...
r[i-1]
r[i]
...
可把r[0]看成是r[i]的备份,以后从r[1]~r[i-1]查找插入位置时可直接同r[0]比较,而且r[i]也可被覆盖了。
因为r[i]复制到r[0]后,可认为已经空出了r[i]。
考虑从后向前比较,只要r[i-1]≤r[i],则r[i]的位置不必改变,否则(即r[i-1]>r[i]),则将r[i-1]移动到r[i]处,然后再比较r[i-2]和r[0],依次等等。
当最后找到一个比r[0]小的关键字时,将r[0]复制到此关键字的后面一个位置,结束。
具体算法为:
voidInsertSort(SqList&L)
{for(i=2;i<=L.length;++i)//第一个元素认为本身有序,所以从第二个元素开始即可
if(L.r[i].key {L.r[0]=L.r[i];//复制L.r[i]至L.r[0],保证下面的for循环肯定能正常结束 for(j=i-1;L.r[0].key L.r[j+1]=L.r[j];//记录后移 L.r[j+1]=L.r[0];//插入到第一个小于L.r[0]的元素L.r[j]的后面 } } 举例: 排序以下序列4938659776132749 过程: (49)38659776132749 (38)(3849)659776132749 (65)(384965)9776132749 (97)(38496597)76132749 (76)(3849657697)132749 (13)(133849657697)2749 (27)(13273849657697)49 (49)(1327384949657697) 该算法的时间复杂度是O(n2),属稳定排序。 直接插入排序算法简单、容易实现。 当待排序记录较少时,排序速度较快,但是,当待排序的记录数量较大时,大量的比较和移动操作将使直接插入排序算法的效率降低;然而,当待排序的数据元素基本有序时,直接插入排序过程中的移动次数大大减少,从而效率会有所提高。 2.折半插入排序 由于直接插入排序的基本操作是向一个有序表中进行查找和插入,因此,其中的“查找”操作可以利用“折半查找”来实现。 由此改进的直接插入排序称为“折半插入排序”。 由于查找是找不到需插入的记录的,因此有一个固定的结论: 待插入位置是查找之后high指示的坐标之后的位置,即high+1,则从high+1到i-1的记录需向后整体平移一个单位,变成high+2~i。 算法入下: voidBInsertSort(SqList&L) {for(i=2;i<=L.length;++i) {L.r[0]=L.r[i]; low=1;high=i-1; while(low<=high) {mid=(low+high)/2; if(L.r[0].key elselow=mid+1; }//查找合适的插入位置 for(j=i-1;j>=high+1;j--) L.r[j+1]=L.r[j]; L.r[high+1]=L.r[0];//high之后的第一个位置就是合适的位置 } } 该算法的时间复杂度是O(n2),属稳定排序。 折半插入排序比直接插入排序明显地减少了关键字间的“比较”次数,但记录“移动”的次数不变。 (三)起泡排序 1.基本思想 起泡排序是交换排序中一种简单的排序方法。 它的基本思想是对所有相邻记录的关键字值进行比效,如果是逆序(a[j]>a[j+1]),则将其交换,最终达到有序化。 其处理过程为: 1>将整个待排序的记录序列划分成有序区和无序区,初始状态有序区为空,无序区包括所有待排序的记录。 2>对无序区从前向后依次将相邻记录的关键字进行比较,若逆序则将其交换,从而使得关键字值小的记录向上“飘浮”(左移),关键字值大的记录好像石块,向下“堕落”(右移)。 每经过一趟冒泡排序,都使无序区中关键字值最大的记录进入有序区,对于由n个记录组成的记录序列,最多经过n-1趟冒泡排序,就可以将这n个记录重新按关键字顺序排列。 2.起泡排序算法 1)原始的冒泡排序算法: 对由n个记录组成的记录序列,最多经过(n-1)趟冒泡排序,就可以使记录序列成为有序序列,第一趟定位第n个记录,此时有序区只有一个记录;第二趟定位第n-1个记录,此时有序区有两个记录;以此类推,算法框架为: (假设用数组a存储待排序记录) voidBubbleSort1(DataTypea,intn) { for(i=n;i>1;i--) { for(j=1;j<=i-1;j++) if(a[j].key>a.[j+1].key) { temp=a[j];a[j]=a[j+1];a[j+1]=temp; } } } 2)改进的冒泡排序算法: 在冒泡排序过程中,一旦发现某一趟没有进行交换操作,就表明此时待排序记录序列已经成为有序序列,冒泡排序再进行下去已经没有必要,应立即结束排序过程。 voidBubbleSort2(DataTypea,intn) { for(i=n;i>1;i--) { exchange=0; for(j=1;j<=i-1;j++) if(a[j].key>a.[j+1].key) {temp=a[j];a[j]=a[j+1];a[j+1]=temp;exchange=1;} if(exchange==0)break; } } 3)进一步地改进冒泡排序算法: 在算法2)给出的冒泡排序算法的基础上,如果我们同时记录第i趟冒泡排序中最后一次发生交换操作的位置m(m<=n-i),就会发现从此位置以后的记录均已经有序,即无序区范围缩小在a[1]~a[m]之间,所以在进行下一趟排序操作时,就不必考虑a[m+1]~a[n]范围内的记录了,而只在a[1]~a[m]范围内进行。 voidBubbleSort3(DataTypea,intn) { last=n-1; for(i=n;i>1;i--) {exchange=0; m=last;//初始将最后进行记录交换的位置设置成i-1 for(j=1;j<=m;j++) if(a[j].key>a.[j+1].key) {temp=a[j];a[j]=a[j+1];a[j+1]=temp; exchange=1; last=j;//记录每一次发生记录交换的位置 } if(exchange==0)break; } } 冒泡排序比较简单,当初始序列基本有序时,冒泡排序有较高的效率,反之效率较低;其次冒泡排序只需要一个记录的辅助空间,用来作为记录交换的中间暂存单元;冒泡排序是一种稳定的排序方法。 举例: 利用冒泡法对以下序列进行排序。 (49,38,65,97,76,13,27,49)n=8 第1趟: 3849657613274997 第2趟: 3849651327497697 第3趟: 3849132749657697 第4趟: 3813274949657697 第5趟: 1327384949657697 第6趟: 1327384949657697 第7趟: 没有必要再比了,因为上一趟(第6趟)没有发生交换,这说明已经排序完毕。 该算法的时间复杂度为O(n2),属于稳定排序。 『真题解析』 1.在执行某个排序算法过程中,出现了排序关键字朝着最终排序序列相反的方向的移动,从而认为该算法是不稳定的。 这种说法对么? 为什么? 【解析】这种看法不对。 本题的叙述与稳定性的定义无关,不能据此来判断排序中的稳定性。 例如5,4,1,2,3在第一趟冒泡排序后得到4,1,2,3,5。 其中4向前移动,与其最终位置相反。 但冒泡排序是稳定排序。 快速排序中无这种现象。 (四)简单选择排序 1.基本思想 简单选择排序的基本思想是: 每一趟在n-i+1(i=1,2,3,...,n-1)个记录中选取关键字最小的记录作为有序序列中的第i个记录。 它的具体实现过程为: 1>将整个记录序列划分为有序区域和无序区域,有序区域位于最左端,无序区域位于右端,初始状态有序区域为空,无序区域含有待排序的所有n个记录。 2>设置一个整型变量index,用于记录在一趟的比较过程中,当前关键字值最小的记录位置。 开始将它设定为当前无序区域的第一个位置,即假设这个位置的关键字最小,然后用它与无序区域中其他记录进行比较,若发现有比它的关键字还小的记录,就将index改为这个新的最小记录位置,随后再用a[index].key与后面的记录进行比较,并根据比较结果,随时修改index的值,一趟结束后index中保留的就是本趟选择的关键字最小的记录位置。 3>将index位置的记录交换到无序区域的第一个位置,使得有序区域扩展了一个记录,而无序区域减少了一个记录。 不断重复2>、3>,直到无序区域剩下一个记录为止。 此时所有的记录已经按关键字从小到大的顺序排列就位。 2.简单选择排序算法 算法如下: voidselecsort(DataTypea,intn) {
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 内部排序 内部 排序