1.归并排序
归并排序与基于交换、选择等排序的思想不一样,“归并”的含义是将两个或两个以上的有序表组合成一个新的有序表。假定待排序表含有n个记录,则可以看成是n个有序的子表,每个子表长度为1,然后两两归并,得到⌈n/2⌉个长度为2或1的有序表;再两两归并,......如此重复,直到合并成一个长度为n的有序表为止,这种排序方法称为2-路归并排序。
Merge()的功能是将前后相邻的两个有序表归并为一个有序表的算法。设两段有序表A[low...mid]、A[mid+1...high]存放在同一顺序表中相邻的位置上,先将它们复制到辅助数组B中。每次从对应B中的两个段取出其对应的表长时(即该段的所有元素已经完全复制到A中),将另一段中的剩余部分直接复制到A中。算法如下:
ElemType *B=(ElemType *)malloc((n+1)*sizeof(ElemType));
void Merge(ElemType A[],int low,int mid,int high) {
for(int k=low; k<=high; k++) {
B[k]=A[k];
}
for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
if(B[i]<=B[j])
A[k]=B[i++];
else
A[k]=B[j++];
}
while(i<=mid)
A[k++]=B[i++];
while(j<=high)
A[k++]=B[j++];
}
一趟归并排序的操作是,调用到⌈n/2h⌉次算法merge()将L[1...n]中前后相邻且长度为h的有序段进行两两归并,得到前后相邻、长度为2h的有序段,整个归并排序需要进行到⌈log2(n)⌉趟。
递归形式的2-路归并排序算法是基于分治的,其过程如下:
分解:将含有n个元素的待排序表分成各含有n/2个元素的子表,采用2-路归并排序算法对两个子表递归地进行排序:
合并:合并两个已排序的子表得到排序结果。
void MergeSort(ElemType A[],int low,int high) {
if(low<high) {
int mid = (low+high)/2;
MergeSort(A,low,mid);
MergeSort(A,mid+1,high);
Merge(A,low,mid,high);
}
}
2-路归并排序算法的性能分析如下:
空间效率:Merge()操作中,辅助空间刚好占用n个单元,所以归并排序的空间复杂度为O(n)。
时间效率:每一趟归并的时间复杂度为O(n),共需要进行到⌈log2(n)⌉趟归并,所以算法的时间复杂度为O(nlog2(n))。
稳定性:由于Merge()操作不会改变相同关键字记录的相对次序,所以2-路归并排序算法是一个稳定的排序算法。
2.基数排序
基数排序是一种很特殊的排序方法,它不是基于比较进行排序的,而是采用多关键字排序思想(即基于关键字各位的大小进行排序的),借助“分配”和“收集”两种操作对单逻辑关键字进行排序。基数排序又分为最高位优先(MSD)排序和最低位优先(LSD)排序。
空间效率:一趟排序需要的辅助存储空间为r(r个队列),但以后的排序中重复使用这些队列,所以基数排序的空间复杂度为O(r)。
时间效率:基数排序需要进行d趟分配和收集,一趟分配需要O(n),一趟收集需要O(r),所以基数排序的时间复杂度为O(d(n+r)),它与序列的初始化状态无关。
稳定性:对于基数排序算法而言,很重要一点就是按位排序时必须是稳定的。因此,这也保证了基数排序保持稳定性。