1、第九章 排序,本章要点排序的基本概念 插入排序、交换排序、选择排序、归并排序、基数排序 内部排序、外部排序 了解内部、外部排序,稳定和不稳定排序等概念, 熟练掌握直接插入排序、冒泡排序、直接选择排序等简单排序。 掌握希尔排序、快速排序、堆排序和归并排序等高效排序。 掌握各种排序方法的时间复杂度分析、学会根据实际问题选择合适排序方法。,9.1排序的基本概念 排序定义就是把一组无序的记录按其关键字的某种次序排列起来,使其具有一定的顺序,便于进行数据查找。 若待排序的文件中,存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,则称这种排序方法是稳定的;反之,若具有相同
2、关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。,排序分类 按待排序记录所在位置 内部排序:在排序过程中,若将整个文件都放在内存中处理,排序时不涉及数据内、外存交换称为内排序。 外部排序:排序过程中需对外存进行访问的排序。 内排序适用记录个数不很多的小文件,外排序则适用于记录个数太多,不能将其全部记录放在内存中的大文件。,按排序依据原则 插入排序:直接插入排序、折半插入排序、希尔排序 交换排序:冒泡排序、快速排序 选择排序:直接(简单)选择排序、堆排序 归并排序:2-路归并排序 基数排序,每一种排序算法可在不同的存储结构上实现:数组(顺序)、链式(动态链表和静态链表)、索引(索
3、引表)。 评价排序算法的好坏标准:1.算法执行所需要的时间2.执行算法所需要的辅助空间 由于排序是经常使用的一种运算,因此排序的时间开销是算法好坏的重要标志。排序的时间开销可以用算法执行中的比较和移动次数来衡量。,排序基本操作(1) 比较两个关键字大小(2)将记录从一个位置移动到另一个位置文件的顺序存储结构typedef int KeyType;typedef struct KeyType key;InfoType otherinfo;RecType;typedef RecType SeqListn+1; 9.2 插入排序 直接插入排序 排序过程:整个排序过程为n-1趟插入,即先将序列中第1个
4、记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序。,直接排序示例,49 38 65 97 76 13 27,i=2 38 38 49 65 97 76 13 27,i=3 65 38 49 65 97 76 13 27,i=4 97 38 49 65 97 76 13 27,i=5 76 38 49 65 76 97 13 27,i=6 13 13 38 49 65 76 97 27,i=1 ,i=7 13 38 49 65 76 97 27,27,97,76,65,49,38,27,理解R0监视哨的两个作用,void InsertSort(SeqList R)i
5、nt i,j;for(i=2;i=n;i+)R0=Ri;j=i-1;while(R0.keyRj.key)Rj+1=Rj;j-;Rj+1=R0;,算法评价 时间复杂度 若待排序记录按关键字从小到大排列(正序) 关键字比较次数:,记录移动次数:,若待排序记录按关键字从大到小排列(逆序) 关键字比较次数:,记录移动次数:,若待排序记录是随机的,取平均值 关键字比较次数:,记录移动次数:,T(n)=O(n),空间复杂度:S(n)=O(1),希尔排序(缩小增量法) 排序过程:先取一个正整数d1n,把所有相隔d1的记录放一组,组内进行直接插入排序;然后取d2d1,重复上述分组和排序操作;直至di=1,即
6、所有记录放进一个组中排序为止,算法描述,void shellpass(SeqList R,int d) for(i=d+1;i0 ,void ShellSort(SeqList R)int increment=n;do increment=increment/3+1;ShellPass(R,increment); while(increment1),#define T 3 int d=5,3,1;,49,13,38,27,27,4,55,38,65,48,97,55,76,4,希尔排序特点 子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列 希尔排序可提高排序速度,因为
7、 分组后n值减小,n更小,而T(n)=O(n),所以T(n)从总体上看是减小了 关键字较小的记录跳跃式前移,在进行最后一趟增量为1的插入排序时,序列已基本有序 增量序列取法 无除1以外的公因子 最后一个增量值必须为1,9.3 交换排序 冒泡排序 排序过程 将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序r1.keyr2.key,则交换;然后比较第二个记录与第三个记录;依次类推,直至第n-1个记录和第n个记录比较为止第一趟冒泡排序,结果关键字最大的记录被安置在最后一个记录上 对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录位置 重复上述过程,直到“在一趟
8、排序过程中没有进行过交换记录的操作”为止,例,38,49,76,97,13,97,27,97,30,97,13,76,76,76,27,30,13,65,27,65,30,65,13,13,49,49,30,49,27,38,27,38,30,38,void BubbleSort(SeqList R) int i, j, exchange;for(i=1;i=i;j-)if(Rj+1.keyRj.key) R0=Rj+1;Rj+1=Rj;Rj=R0;exchange=1;if(!exchange)break;,算法评价 时间复杂度 最好情况(正序) 比较次数:n-1 移动次数:0 最坏情况(逆
9、序) 比较次数:,T(n)=O(n),空间复杂度:S(n)=O(1),快速排序 基本思想:通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序 排序过程:对rst中记录进行一趟快速排序,附设两个指针i和j,设枢轴记录rp=rs,x=rp.key 初始时令i=s,j=t 首先从j所指位置向前搜索第一个关键字小于x的记录,并和rp交换 再从i所指位置起向后搜索,找到第一个关键字大于x的记录,和rp交换 重复上述两步,直至i=j为止 再分别对两个子序列进行快速排序,直到每个子序列只含有一个记录为止,完成一趟
10、排序: ( 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,算法描述,void qksort(rectype r,int t,int w) int i,j,k;rectype x;if(t=w) return;i=t; j=w; x=ri;while(i=x.key) j-;,if(ij) ri=rj; i+; while(ij),算法评价 时间复杂度 最好情况(每次总是选到中间值作枢轴)T(
11、n)=O(nlog2n) 最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n),T(n)=O(n),空间复杂度:需栈空间以实现递归 最坏情况:S(n)=O(n) 一般情况:S(n)=O(log2n),9.4 选择排序 直接选择排序 排序过程 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换 重复上述操作,共进行n-1趟排序后,排序结束,例,初始: 49 38 65 97 76 13 27 ,i=1,13,49,一趟: 13 38 65 97 76 49 27 ,i=
12、2,27,38,六趟: 13 27 38 49 65 76 97 ,排序结束: 13 27 38 49 65 76 97,算法描述,void SelectSort(SeqList R) int i,j,k;for(i=1;in;i+) k=i;for(j=i+1;j=n;j+)if(Rj.keyRk.key) k=j;if(k!=i) R0=Ri; Ri=Rk; Rk=R0; ,算法评价 时间复杂度 记录移动次数 最好情况:0 最坏情况:3(n-1) 比较次数:,T(n)=O(n),空间复杂度:S(n)=O(1),堆排序 堆的定义:n个元素的序列(k1,k2,kn),当且仅当满足下列关系时,称
13、之为堆,例 (96,83,27,38,11,9),例 (13,38,27,50,76,65,49,97),可将堆序列看成完全二叉树,则堆顶 元素(完全二叉树的根)必为序列中 n个元素的最小值或最大值,堆排序:将无序序列建成一个堆,得到关键字最小(或最大)的记录;输出堆顶的最小(大)值后,使剩余的n-1个元素重又建成一个堆,则可得到n个元素的次小值;重复执行,得到一个有序序列,这个过程叫 堆排序需解决的两个问题: 如何由一个无序序列建成一个堆? 如何在输出堆顶元素之后,调整剩余元素,使之成为一个新的堆? 第二个问题解决方法筛选 方法:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左
14、、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”,例,int sift(rectype r,int k,int m) int i,j;rectype x;i=k; x=ri; j=2*i;while(jrj+1.key)j+;if(x.keyrj.key), ri=rj;i=j;j*=2;else j=m+1;ri=x; ,第一个问题解决方法 方法:从无序序列的第n/2个元素(即此无序序列对应的完全二叉树的最后一个非终端结点)起,至第一个元素止,进行反复筛选,例 含8个元素的无序序列(49,38,65,97,76
15、,13,27,50),算法描述,void heapsort(rectype r,int n) int i;rectype x;for(i=n/2;i=1;i-)sift(r,i,n);for(i=n;i=2;i-) x=r1;r1=ri;,ri=x;sift(r,1,i-1); ,算法评价 时间复杂度:最坏情况下T(n)=O(nlogn) 空间复杂度:S(n)=O(1),9.5 归并排序 归并将两个或两个以上的有序表组合成一个新的有序表,叫 2-路归并排序 排序过程 设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1 两两合并,得到n/2个长度为2或1的有序子序列 再两两合并
16、,如此重复,直至得到一个长度为n的有序序列为止,例,初始关键字: 49 38 65 97 76 13 27,一趟归并后: 38 49 65 97 13 76 27,二趟归并后: 38 49 65 97 13 27 76,三趟归并后: 13 27 38 49 65 76 97,算法描述,void mergesort(rectype r,int n) int i,s=1;rectype tM;while(sn) tgb(s,n,r,t);s*=2;if(sn) tgb(s,n,t,r); s*=2; else i=1;while(i=n) ri=ti+; ,void tgb(int s,int n
17、,rectype r,rectype t) int i=1;while(i=(n-2*s+1) merge(r,i,i+s-1,i+2*s-1,t);i=i+2*s;if(i(n-s+1) merge(r,i,i+s-1,n,t);else while(i=n) ti=ri+; ,if(im)while(j=w) t+k=rj+;elsewhile(i=m) t+k=ri+; ,void merge(rectype r,int h,int m,int w,rectype t) int i,j,k;i=h; j=m+1; k=h-1;while(i=m),9.6 基数排序 多关键字排序 定义:,
18、例 对52张扑克牌按以下次序排序: 23A23A 23A23A 两个关键字:花色( )面值(23A) 并且“花色”地位高于“面值”,多关键字排序方法 最高位优先法(MSD):先对最高位关键字k1(如花色)排序,将序列分成若干子序列,每个子序列有相同的k1值;然后让每个子序列对次关键字k2(如面值)排序,又分成若干更小的子序列;依次重复,直至就每个子序列对最低位关键字kd排序;最后将所有子序列依次连接在一起成为一个有序序列 最低位优先法(LSD):从最低位关键字kd起进行排序,然后再对高一位的关键字排序,依次重复,直至对最高位关键字k1排序后,便成为一个有序序列,MSD与LSD不同特点 按MSD
19、排序,必须将序列逐层分割成若干子序列,然后对各子序列分别排序 按LSD排序,不必分成子序列,对每个关键字都是整个序列参加排序;并且可不通过关键字比较,而通过若干次分配与收集实现排序,链式基数排序 基数排序:借助“分配”和“收集”对单逻辑关键字进行排序的一种方法 链式基数排序:用链表作存储结构的基数排序,链式基数排序步骤 设置10个队列,fi和ei分别为第i个队列的头指针和尾指针 第一趟分配对最低位关键字(个位)进行,改变记录的指针值,将链表中记录分配至10个链队列中,每个队列记录的关键字的个位相同 第一趟收集是改变所有非空队列的队尾记录的指针域,令其指向下一个非空队列的队头记录,重新将10个队
20、列链成一个链表 重复上述两步,进行第二趟、第三趟分配和收集,分别对十位、百位进行,最后得到一个有序序列,例,算法描述,#define D 3 typedef struct node int key;float info;int link; rectype;,int radixsort(rectype r,int n) int i,j,k,t,p,rd,rg,f10,e10;for(i=1;in;i+) ri.link=i+1;rn.link=0;p=1; rd=1; rg=10;for(i=1;i=D;i+) for(j=0;j10;j+) fj=0;ej=0;,dok=(rp.key%rg)
21、/rd;if(fk=0)fk=p;else rek.link=p;ek=p;p=rp.link;while(p0);j=0;while(fj=0) j+;p=fj; t=ej;,for(k=j+1;k0) rt.link=fk;t=ek;rt.link=0;rg*=10;rd*=10;return(p); ,算法评价 时间复杂度: 分配:T(n)=O(n) 收集:T(n)=O(rd) T(n)=O(d(n+rd) 其中:n记录数d关键字数rd关键字取值范围 空间复杂度:S(n)=2rd个队列指针+n个指针域空间,f0=0 e0=0 f1=0 e1=0 f2=0 e2=0 f3=0 e3=0 f
22、4=0 e4=0 f5=0 e5=0 f6=0 e6=0 f7=0 e7=0 f8=0 e8=0 f9=0 e9=0,1,1,2,2,3,3,4,4,5,6,6,7,7,8,9,10,f0=0 e0=0 f1=0 e1=0 f2=0 e2=0 f3=0 e3=0 f4=0 e4=0 f5=0 e5=0 f6=0 e6=0 f7=0 e7=0 f8=0 e8=0 f9=0 e9=0,1,3,4,4,7,7,9,10,3,10,1,6,2,5,8,f0=0 e0=0 f1=0 e1=0 f2=0 e2=0 f3=0 e3=0 f4=0 e4=0 f5=0 e5=0 f6=0 e6=0 f7=0 e7=0 f8=0 e8=0 f9=0 e9=0,4,4,7,9,2,8,7,9,2,8,3,1,10,6,5,