8-8归并排序

思想:将两个或多个已经有序的子序列合并为一个整体有序的序列
在这里插入图片描述
一.概念引入

1.二路归并
(由小到大)
准备三个指针和一个空表

int i, j, k;
int n = 12;
int *b = (int*)malloc(n * sizeof(int));

在这里插入图片描述
对比i和j元素大小, 选择小的放入新表。此处选择7,j,k指针后移

在这里插入图片描述
对比当前i和j所指元素的大小,将10放入,j,k后移
在这里插入图片描述
直到两个相同的24,任意选一个放入新表,这里选i指向的
在这里插入图片描述
再将另一个24放入新表
在这里插入图片描述
33放入,37放入
在这里插入图片描述
j超出下标范围,将i及其后面的元素依次放入新表
在这里插入图片描述
二路归并每次对比一次关键字就能选择出一个最小的元素放入正确位置

2.四路归并
在这里插入图片描述
p1和p2选出来一个小的,和p3比,选出来小的,和p4比,通过3次比较即可选出最小
因此四路归并比较3次
因此m路归并,每选出一个元素需要对比关键字m-1次

二.基于二路归并的归并排序
(一)基本步骤
初始将每个元素都看成是一个已经有序的部分,两两归并
在这里插入图片描述

在这里插入图片描述
再将新的几个部分两两归并
在这里插入图片描述
再将新的几个部分两两归并
在这里插入图片描述
(二)代码实现
定义指针low,high,mid
初始low指向0号位,high指向6号位,由主函数传值
mid = (low + high) / 2 (默认向下取整)
在这里插入图片描述
递归的对左右两个子表再分割

void MergeSort(int a[], int low, int high)
{
	if (low < high)
	{
		MergeSort(a, low, mid);//a为图中的A[]数组
		MergeSort(a, mid + 1, high);
	}
}

对左子表的分割如下
low指向0,high指向3,mid指向1
在这里插入图片描述
low指向0,high指向1,mid指向0
在这里插入图片描述
low指向0,high指向0,不满足low<high,完成分割

此时每一个元素是一个小的部分,将low、mid、high传入Merge(a, low, mid, high)进行排序

Merge函数:将每个部分按顺序复制到新表,然后按照二路归并的原理移动指针,依次选择更小的元素放回原数组。
在这里插入图片描述
可以看出,Merge函数是对两个已经有序的序列进行归并,下面举例说明代码过程

Merge函数接收从MergeSort传入的a, low, mid, high,对两部分归并

在这里插入图片描述
创建新表(数组)

int n = 7;
int* b = (int*)malloc(n * sizeof(int));

设置指针与复制元素

int i, j, k;
for (int k = low; k <= high; k++)
		b[k]=a[k];

指针赋值

i = low, j = mid + 1, k = i

在这里插入图片描述
注:因新表是两个表按顺序复制来的,为保证稳定性,两个24需先选择左边的(i指向的)24放回原表

排序结果
在这里插入图片描述
运行条件(直到某一方结束)

i <= mid && j <= high

比较与放回原表

if (b[i] <= b[j]) 
	  a[k] = b[i++];
else 
	  a[k] = b[j++];

剩余元素处理

while (i <= mid) a[k++] = b[i++];//i部分剩余
while (j <= high) a[k++] = b[j++];//j部分剩余

完整代码

#include<stdint.h>
#include<iostream>
using namespace std;
int n = 5;
int *b = (int*)malloc(n * sizeof(int));
void Merge(int a[],int low,int mid,int high)
{
	int i, j, k;
	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++];
}
void MergeSort(int 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);
	}
}
int main()
{
	int a[5] = { 431,264,46,822,9645 };
	MergeSort(a, 0, 4);
	for (int i = 0; i < 5; i++)
	{
		cout << a[i] << " ";
	}
}

三.效率分析
1.整个过程可以看成是一棵倒着的树,设树高是h,一共归并了h-1趟。第h层最多 2 h − 1 2^{h-1} 2h1个结点,初始序列作为树的最深层,元素个数n一定≤ 2 h − 1 2^{h-1} 2h1,因此h-1≥ l o g 2 n log_2n log2n。当结点数为 2 h − 1 2^{h-1} 2h1时,h-1=⌈ l o g 2 n log_2n log2n⌉=归并排序的趟数。每一趟的归并中,每对比一次都可选择一个元素放到正确位置,因此最多对比关键字n-1次,时间复杂度为O(n)。因此总的时间复杂度为O(n l o g 2 n log_2n log2n)
2.空间复杂度=递归调用栈(=h-1=归并趟数)+辅助数组=O( l o g 2 n log_2n log2n)+O(n)=O(n)
3.稳定性:当出现关键字相等时选择i指向的元素优先放回,因此是稳定的
在这里插入图片描述
四.总结
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡__卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值