内部排序 (五):2-路归并排序 (2-way Merge Sort)

作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!

归并

  • 归并:将两个或两个以上的有序表组合成一个新的有序表
    在这里插入图片描述
    • 初始: j = m + 1 , k = i j=m+1, k=i j=m+1,k=i
    • i < = m   & &   j < = n i<=m\ \&\&\ j<=n i<=m && j<=n 时,若 S R [ i ] . k e y ≤ S R [ j ] . k e y SR[i].key\leq SR[j].key SR[i].keySR[j].key T R [ k ] = S R [ i + + ] TR[k] = SR[i++] TR[k]=SR[i++];否则 T R [ k ] = S R [ j + + ] TR[k] = SR[j++] TR[k]=SR[j++] k + + k++ k++
    • 如有剩余记录,复制
void Merge(Rec_t* src, Rec_t* tmp, int i, int m, int n)
{
	int j = m + 1;
	int k = i;

	while (i <= m && j <= n)
	{
		if (src[i].key <= src[j].key)
		{
			tmp[k++] = src[i++];
		}
		else {
			tmp[k++] = src[j++];
		}
	}
	while (i <= m)
	{
		tmp[k++] = src[i++];
	}
	while (j <= m)
	{
		tmp[k++] = src[j++];
	}
}

思路

  • 通过归并两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度

  • 设初始序列含有 n n n 个记录,则可看成 n n n 个有序的子序列,每个子序列长度为 1
  • 两两合并,得到 ⌈ n / 2 ⌉ \lceil n/2\rceil n/2 个长度为 2 或 1 的有序子序列
  • 重复上一步,直至得到一个长度为 n n n 的有序序列为止
    在这里插入图片描述

递归实现

  • 2-路归并排序的递归写法非常简单, 只需要反复将当前区间分为两半, 对两个子区间分别递归进行归并排序, 然后将两个已经有序的子区间合并为有序序列即可
    • S R SR SR 平分成两个无序子序列 S R [ s . . m ] SR[s..m] SR[s..m] S R [ m + 1.. t ] SR[m+1..t] SR[m+1..t]
    • S R [ s . . m ] SR[s..m] SR[s..m] 归并为有序 T R 2 [ s . . m ] TR2[s..m] TR2[s..m]
    • S R [ m + 1.. t ] SR[m+1..t] SR[m+1..t] 归并为有序 T R 2 [ m + 1.. t ] TR2[m+1..t] TR2[m+1..t]
    • 调用 Merge() 将有序 T R 2 [ s . . m ] TR2[s..m] TR2[s..m] T R 2 [ m + 1.. t ] TR2[m+1..t] TR2[m+1..t] 合并为有序
      在这里插入图片描述
void M_sort_recursive(Rec_t* src, Rec_t* dst, int s, int t)
{
	static Rec_t tmp[MAX_SIZE + 1];

	if (s == t)
	{
		dst[s] = src[s];
	}
	else {
		int mid = (s + t) / 2;
		
		M_sort_recursive(src, tmp, s, mid);
		M_sort_recursive(src, tmp, mid + 1, t);
		Merge(tmp, dst, s, mid, t);
	}
}

//2-路归并排序 递归实现
void Merge_sort_recursive(SqList_t* list)
{
	M_sort_recursive(list->rec, list->rec, 1, list->len);
}

非递归实现

  • (1) 先将待排序列中有序子列的长度看作 1
  • (2) 对待排序列中所有有序子列进行两两归并,此时待排序列中有序子列的长度翻倍
  • (3) 重复上一步,直至有序子列长度大于等于待排序列的长度
// n 为元素个数,len 为当前有序子列的长度
// 该函数对所有有序子列进行归并
void Merge_pass(Rec_t* src, Rec_t* tmp, int n, int len)
{
	int i;
	for (i = 1; i + 2 * len <= n; i += len * 2)
	{
		Merge(src, tmp, i, i + len - 1, i + 2 * len - 1);
	}
	if (i + len <= n) // 归并最后两个有序子列
	{
		Merge(src, tmp, i, i + len - 1, n);
	}
	else { // 归并最后一个有序子列
		while (i <= n)
		{
			tmp[i] = src[i];
			++i;
		}
	}
}

void Merge_sort(SqList_t* list)
{
	Rec_t* tmp = (Rec_t*)malloc((list->len + 1) * sizeof(Rec_t));
	int len = 1; 	// 当前有序子列的长度
	
	while (len < list->len)
	{
		Merge_pass(list->rec, tmp, list->len, len);
		len *= 2;
		Merge_pass(tmp, list->rec, list->len, len);
		len *= 2;
	}

	free(tmp);
}

算法评价

T ( n ) T(n) T(n)

T ( n ) = 2 T ( n 2 ) + c n ∴ T ( n ) = O ( n log ⁡ n ) T(n)=2T(\frac{n}{2}) + cn \\\therefore T(n)=O(n\log n) T(n)=2T(2n)+cnT(n)=O(nlogn)

S ( n ) S(n) S(n)

S ( n ) = O ( n ) S(n)=O(n) S(n)=O(n)

是否稳定

  • 稳定

总结

  • 因为要在空间之间来回复制数据,效率比较低,因此一般很少利用 2-路归并排序进行内部排序,特别是递归形式。它一般用在外部排序中
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值