数据结构----二路归并排序

本文详细介绍了二路归并排序的基本思想和实现过程,包括一次二路归并的步骤以及整个排序算法的逻辑。通过示例代码展示了如何进行相邻有序子数组的合并,并分析了算法的时间复杂度为O(nlgn),空间复杂度为O(n),证明其为一种稳定的排序算法。此外,还提供了测试代码以验证算法的正确性。
摘要由CSDN通过智能技术生成

二路归并排序的基本思想

设数组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),而且还是一种稳定的排序算法。这是二路归并排序算法的最大特点。

  • 18
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值