3.2.2 归并排序

3.2.2 归并排序问题

该方法的基本思想是:
在这里插入图片描述
若k=2,即归并在相邻的两个有序子表中进行的,称为二路归并排序。
若k>2,即归并操作在相邻的多个有序子表中进行,则叫多路归并排序。

1.自底向上的二路归并排序算法

例如,对于{2,5,1,7,10,6,9,4,3,8}序列,其排序过程如下图所示,图中方括号内是一个有序子序列。在这里插入图片描述

二路归并排序的分治策略:

循环【log2n】次,length依次取1、2、…、log2n。每次执行以下步骤:
① 分解:将原序列分解成length长度的若干子序列。
② 求解子问题:将相邻的两个子序列调用Merge算法合并成一个有序子序列。
③ 合并:由于整个序列存放在数组中a中,排序过程是就地进行的,合并步骤不需要执行任何操作。
在这里插入图片描述

MergeSort (int a[ ],int n)
void MergeSort(int a[],int n) //序列a有n个元素
{ 	int length; //每一次合并的时候子序列的长度是length
 	for(length=1;length<n;length=2*length)//lenth= 1时:把长度为n的序列看成n个长度为1的子序列。对其中相近的子系列进行归并排序
	MergePass(a,length,n);/子序列的长度为length=1进行排序,总共有n个元素
}

①length=1的时候,两个两个排序, 循环次数:n/2 一共有5次
在这里插入图片描述
②length=2×1== 2的时候 子序列长度为2。相邻子序列长度为2的进行归并得到:(1,2,5,7),(4,6,9,10)
在这里插入图片描述
③length=2×2== 4的时候 相邻子序列长度为4的进行归并得到:(1,2,5,7,4,6,9,10),该子序列长度为8
在这里插入图片描述
③length=2×4==8的时候 Mergepass(a,8,10)合并成一个序列
在这里插入图片描述

合并操作用的是: MergePass(int a[ ],int length,int n)
void MergePass(int a[],int length,int n)//序列的起始地址,要合并的子序列的长度,序列a的元素的个数
 //一趟二路归并排序
{ 	int i;
 	for(i=0;i+2*length-1<n;i=i+2*length) 
//归并length长的两相邻子表
		 Merge(a,i,i+length-1,i+2*length-1);//a:序列的首地址,i:第一个序列起始下表,i+length-1:第一个序列结束的下表,i+2*length-1:序列结束的地址 
	 if(i+length-1<n) //余下两个子表,后者长度小于length
		 Merge(a,i,i+length-1,n-1); //归并这两个子表。
 
}
void Merge(int a[],int low,int mid,int high)
void Merge(int a[]int low,int mid,int high)
 //a[low..mid]和a[mid+1..high]→a[low..high]
{ int *tmpa;
 int i=low,j=mid+1,k=0;
 tmpa=(int *)malloc((high-low+1)*sizeof(int));
 while (i<=mid && j<=high)
 if (a[i]<=a[j]) //将第1子表中的元素放入tmpa中
 { tmpa[k]=a[i]; i++; k++; }
 else //将第2子表中的元素放入tmpa中
 { tmpa[k]=a[j]; j++; k++; }
 while (i<=mid) //将第1子表余下部分复制到tmpa
 { tmpa[k]=a[i]; i++; k++; }
 while (j<=high) //将第2子表余下部分复制到tmpa
 { tmpa[k]=a[j]; j++; k++; }
 for (k=0,i=low;i<=high;k++,i++) //将tmpa复制回a中
 a[i]=tmpa[k];
 free(tmpa); //释放tmpa所占内存空间
}

【算法分析】当有n个元素时,需要log2n趟归并,每一趟归并, 其元素比较次数不超过n-1,元素移动次数都是n,因此归并排序的时间复杂度为O(nlog2n)。

2.自上向下的二路归并排序算法

在这里插入图片描述
设归并排序的当前区间是a[low…high],则递归归并的两个步骤:
① 分解:将序列a[low…high]一分为二,即求mid=(low+high)/2;
递归地对两个子序列a[low…mid]和a[mid+1…high]进行继续分解。
其终结条件是子序列长度为1(因为一个元素的子表一定是有序表)。
② 求解子问题:排序两个子序列a[low…mid]和a[mid+1…high]。
③ 合并:与分解过程相反,将已排序的两个子序列a[low…mid]和a[mid+1…high]归并为一个有序序列a[low…high]。
对应的二路归并排序算法:

void MergeSort(int a[],int low,int high) //二路归并算法
{ int mid;
 if(low<high) //子序列有两个或以上元素
 { mid=(low+high)/2; //取中间位置
 MergeSort(a,low,mid); //对a[low..mid]子序列排序
 MergeSort(a,mid+1,high); //对a[mid+1..high]子序列排序
 Merge(a,low,mid,high); //将两子序列合并,见前面的算法
 } }

递归出口为序列长度为1或者0!

【 算法分析】:
设MergeSort(a,0,n-1)算法的执行时间为T(n)
Merge(a,0,n/2,n-1)的执行时间为O(n)

T(n)=1 当n=1
T(n)=2T(n/2)+O(n) 当n>1

推出:T(n)=O(nlog2n)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值