数据结构----二路归并排序
二路归并排序的基本思想设数组a中存放了n个数据元素,初始时把它们看成是n个长度为1的有序子数组,然后从第一个有序子数组开始,把相邻的有序子数组两两合并,得到[n/2]个长度为2的新的有序子数组(当n为奇数时,最后一个新的有序子数组的长度为1)。对这些新的有序子数组再进行两两归并。如此重复,直到得到一个长度为n的有序数组为止。一次二路归并排序算法的目标是把若干个长度为k的相邻有序子数组从前、向后进行
二路归并排序的基本思想
设数组a中存放了n个数据元素,初始时把它们看成是n个长度为1的有序子数组,然后从第一个有序子数组开始,把相邻的有序子数组两两合并,得到[n/2]个长度为2的新的有序子数组(当n为奇数时,最后一个新的有序子数组的长度为1)。对这些新的有序子数组再进行两两归并。如此重复,直到得到一个长度为n的有序数组为止。
一次二路归并排序算法的目标是把若干个长度为k的相邻有序子数组从前、向后进行两两归并,得到个数减半的长度为2k的相邻有序子数组。算法设计中要考虑的一个问题是:若元素个数为2k的整数倍,则两两归并正好完成n个数据元素的一次二路归并;若元素个数不为2k的整数倍,则当归并到最后一组时,剩余的元素个数会不足2k个,这时的处理方法是:
① 若剩余的元素个数大于k而小于2k,则把前k个元素作为一个子数组,把剩余的元素作为最后一个子数组。
② 若剩余的元素个数小于k时,也即剩余的元素个数只够一组时,则不用再进行两两归并排序。
一次二路归并
void Merge(DataType a[], int n, DataType swap[], int k){
//一次二路归并排序后的有序子序列存于数组swap中,k为有序子数组的长度
int m = 0, u1, u2, l2, i, j;
int l1 = 0;//第一个有序子数组的下界为0;
while(l1+k <=n-1){
l2 = l1+k;//计算第二个有序数组的下界
u1 = l2-1;//计算第一个有序数组的上界
u2 = (l2+k-1 <=n-1) ? l2+k-1:n-1;//计算第二个有序数组上界
for(i=l1,j=l2;i<u1&&j<=u2;m++){//两个有序子数组合并
if(a[i].key<=a[j].key){
swap[m] = a[i];
i++;
}else{
swap[m] = a[j];
j++;
}
}
while(i<=u1){//子数组2已经归并完,将子数组1中剩余的元素存放到数组swap中
swap[m] = a[i];
m++;
i++;
}
while(j<=u2){//子数组1已经归并完,将子数组2中剩余的元素存放到数组swap中
swap[m] = a[j];
m++;
j++;
}
l1 = u2+1;
}
for(i=l1;i<n;i++,m++){//将原始数组中只够一组的数据元素顺序存放到数组swap中
swap[m] = a[i];
}
}
二路归并排序
void MergeSort(DataType a[], int n){
int i, k=1;//归并长度从1开始
DataType *swap;
swap = (DataType *)malloc(sizeof(DataType)*n);//申请动态数组空间
while(k<n){
Merge(a,n,swap,k);//调用归并函数
for(i=0;i<n;i++){
a[i] = swap[i];//将元素从临时数组swap放回数组a中
}
k = 2*k;//归并长度加倍
}
free(swap);//释放动态数组空间
}
测试代码
#include <stdio.h>
#include <stdlib.h>
typedef int KeyType;
typedef struct{
KeyType key;
}DataType;
void Merge(DataType a[], int n, DataType swap[], int k){
//一次二路归并排序后的有序子序列存于数组swap中,k为有序子数组的长度
int m = 0, u1, u2, l2, i, j;
int l1 = 0;//第一个有序子数组的下界为0;
while(l1+k <=n-1){
l2 = l1+k;//计算第二个有序数组的下界
u1 = l2-1;//计算第一个有序数组的上界
u2 = (l2+k <=n-1) ? l2+k-1:n-1;//计算第二个有序数组上界
for(i=l1,j=l2;i<u1&&j<=u2;m++){//两个有序子数组合并
if(a[i].key<=a[j].key){
swap[m] = a[i];
i++;
}else{
swap[m] = a[j];
j++;
}
}
while(i<=u1){//子数组2已经归并完,将子数组1中剩余的元素存放到数组swap中
swap[m] = a[i];
m++;
i++;
}
while(j<=u2){//子数组1已经归并完,将子数组2中剩余的元素存放到数组swap中
swap[m] = a[j];
m++;
j++;
}
l1 = u2+1;
}
for(i=l1;i<n;i++,m++){//将原始数组中只够一组的数据元素顺序存放到数组swap中
swap[m] = a[i];
}
}
void MergeSort(DataType a[], int n){
int i, k=1;//归并长度从1开始
DataType *swap;
swap = (DataType *)malloc(sizeof(DataType)*n);//申请动态数组空间
while(k<n){
Merge(a,n,swap,k);//调用归并函数
for(i=0;i<n;i++){
a[i] = swap[i];//将元素从临时数组swap放回数组a中
}
k = 2*k;//归并长度加倍
}
free(swap);//释放动态数组空间
}
void main(void){
int i=0;
DataType array[9]={72,73,71,23,94,16,5,68,64};
DataType array2[9];
printf("原数组顺序:");
for(i=0;i<9;i++){
printf("%d ",array[i]);
}
printf("\n\n");
MergeSort(array,9);
printf("二路归并排序之后结果:");
for(i=0;i<9;i++){
printf("%d ",array[i]);
}
getch(0);
}
上图所示是二路归并排序算法各次归并排序过程的一个示例。
复杂度分析
对n个元素进行一次二路归并排序时,归并的次数约为lbn,任何一次的二路归并排序元素的比较次数都约为n-1,所以,二路归并排序算法的时间复杂度为O(nlbn)。
二路归并排序时使用了n个临时内存单元存放数据元素,所以,二路归并排序算法的空间复杂度为O(n)。
由于二路归并排序算法是相邻有序子表两两归并,对于关键字相同的数据元素,能够保证原来在前边的元素排序后仍在前边,因此,二路归并排序算法是一种稳定的排序算法。
前边讨论过的几个时间复杂度为O(nlbn)的排序算法都是不稳定的排序算法,而二路归并排序算法不仅时间复杂度是O(nlbn),而且还是一种稳定的排序算法。这是二路归并排序算法的最大特点。
更多推荐
所有评论(0)