10.5 归并排序
归并排序的基本思想是:将两个或两个以上的有序子序列“归并”为一个有序序列。
在内部排序中,通常采用的是2-路归并排序。即:将两个位置相邻的有序子序列
43_001 |
void Merge(Elem SR[], Elem TR[], int i, int m, int n) { for(j = m+1, k = i; i <= m && j <= n; ++ k) { //将SR中记录由小到大地并入TR if(SR[i].key <= SR[j].key) TR[k] = SR[i++]; else Tr[k] = SR[j++]; } if(i <= m) TR[k,..n] = S[i,...m]; //将剩余的SR[i,...m]复制到TR if(j <= n) TR[k,..n] = S[j,...n]; //将剩余的SR[j,...n]复制到TR }//Merge |
归并排序的算法
如果记录无序序列R[s,…t]的两部分分别按关键字有序,则利用上述归并算法很容易将它们归并成整个记录序列是一个有序序列,由此,应该先分别对这两部分进行2-路归并排序。
例如:
52,23,80,36,68,14 (s=1,t=6)
[52,23,80],[36,68,14]
[52,23][80],[36,68][14]
[52][23] ,[36][68]
[23,52] ,[36,68]
[23,52,80],[14,36,68]
[14,23,36,52,68,80]
43_003 |
void Msort(Elem SR[], Elem TR1[], int s, int t) { if(s == t) TR1[s] = SR[s]; else { m = (s+t) / 2; //将SR[s,t]平分为SR[s,m]和SR[m+1,t] Msort(SR, TR2, s, m); //递归地将SR[s,m]归并为有序的TR2[s,m] Msort(SR, TR2, m+1, t); //递归第将SR[s,m]归并为有序的TR2[s,m] Merge(TR2, TR1, s, m, t); //将TR[s,m]和TR2[m+1,t]归并到TR1[s,t] } }//MSort
void MergeSort(Elem R[]) { //对记录序列R[1,n]做2-路归并排序 Msort(R, R, 1, n); }//MergeSort |
容易看出,对n个记录进行归并排序的时间复杂度为O(nlogn)。即:每一趟归并的时间复杂度为O(n),总共需进行趟。
10.6 基数排序
借助“多关键字排序”的思想来实现“单关键字排序”的算法。
一、多关键字排序
N个记录序列{R1,R2,…,Rn}对关键有序是指:对于序列中任意两个记录Ri和Rj ,(1<=i<=j<=n)都满足下列(词典)有序关系:
其中
实现多关键字排序通常有两种做法:
最高位优先MSD法:先对K0进行排序,并按K0的不同值将记录序列分成若干子序列之后,分别对K1进行排序,…,依次类推,直至最后对最次位关键字排序完成为止。
最低位优先LSD法:先对Kd-1进行排序,并按Kd-2的不同值将记录序列分成若干子序列之后,分别对K0进行排序,…,依次类推,直至最后对最次位关键字排序完成为止。
排序过程中不需要根据“前一个”关键字的排序结果,将记录分割成若干个(“前一个“关键字不同的)子序列。
例如:学生记录含三个关键字:系别、班号和班内的序列号,其中以系别为最主关键字。LSD的排序过程如下:
二、链式基数排序
假如多关键字的记录序列中,每个关键字的取值范围相同,则按LSD法进行排序时,可以采用“分配-收集”的方法,其好处是不需要进行关键字间的比较。
对于数字型或字符型的单关键字,可以看成是由多个数位或多个字符构成的多关键字,此时可以采用这种“分配-收集”的办法进行排序,称作基数排序法。
例如对下列这组关键字
{209,386,768,185,247,606,230,834,539}
首先按其“个位数”取值分别为0,1,…,9“分配”成10组,之后按从0至9的顺序将它们“收集”在一起,然后按其“十位数”取值分别为0,1,…,9“分配”成10组,之后在按从0至9的顺序将它们“收集”在一起;最后按其“百位数”重复一边上述操作,便可得到这组关键字的有序序列。
在计算机上实现基数排序时,为减少所需辅助存储空间,应采用链表作存储结构,即链式基数排序,具体做法为:
1. 带排序记录以指针相链,构成一个链表;
2. “分配”时,按当前“关键字位“索取值,将记录分配到不同的“链队列”中,每个队列中记录的“关键字位”相同。
3. “收集”时,按当前关键字位取值从小到大将各队列首尾相链成一个链表。
4. 对每个关键字均重复2和3两步
提醒注意:
1.“分配”和“收集”的实际操作仅为修改链表中的指针和设置队列的头、尾指针;
2.为查找使用,该链表尚需应用算法Arrange将它调整为有序表。
基数排序的时间复杂度为O(d(n+rd)),其中分配为O(n);收集为O(rd)(rd为“基”),D为“分配-收集”的趟数
10.7各种排序方法的综合比较
一、时间性能
1.平均的时间性能
时间复杂度O(nlogn):快速排序、堆排序和归并排序
时间复杂度为O(n2):直接插入排序、起泡排序和、简单选择排序
时间复杂度为O(n):基数排序。
1. 当待排记录序列按关键字顺序有序时,直接插入排序和起泡排序能达到O(n)的时间复杂度,快速排序的时间性能蜕化为O(n2)
2. 简单选择排序、堆排序和归并排序的时间性能不随记录序列中关键字的分布而改变。
二、空间性能
指的是排序过程中所需的辅助空间大小。
1. 所有的简单排序方法(包括“直接插入、起泡和简单选择)和堆排序的空间复杂度为O(1);
2. 快速排序为O(logn),为栈所需的辅助空间;
3. 归并排序所需辅助空间最多,其空间复杂度为O(n);
4. 链式基数排序所需附设队列首尾指针,怎空间复杂度为O(rd);
三、排序方法的稳定性能
1.稳定的排序方法指的是,对于两个关键字相等的记录,它们在序列中的相对位置,在排序之前和经过排序之后,没有改变。
例如:
(56,34,47,23,66,18,82,47)
(18,23,34,47,47,56,66,82) 稳定的
(18,23,34,47,47,56,66,82) 不稳定的
1. 当对多关键字的记录序列进行LSD方法排序时,必须采用稳定的排序方法。
2. 对于不稳定的排序方法,只要能举出一个实例说明即可。
3. 快速排序、堆排序和希尔排序是不稳定的