【数据结构排序算法(五)】二路归并排序

基础数据结构之八大排序算法(五)

⑤二路归并排序:

时间复杂度:O(nlogn) 外层函数需要遍历的次数与2的指数次有关(外层的时将复杂度为O(logn)),内层函数需要完全遍历所有数据(内层的时间复杂度为O(n)),两个相乘整体的时间复杂度为O(nlogn)
空间复杂度:O(nlogn) 额外的辅助变量会影响的问题的规模(内层额外辅助空间brr,外层函数所需额外辅助变量i),整体空间复杂度为O(nlogn)
稳定性:不稳定 存在跳跃交换

(以顺序表作为待排序对象)

1.基本思想
(1).一开始将所有数据看作单独的个体(队列),这个时候他们都独自有序。
(2).两个对列两个队列融合(保证融合后有序稳定(不存在跳跃交换))
(3).重复第二步直到所有数据都到一个队列中。

以数组arr[]={21,12,7,9,11,98,56,88,66,68}为例:

在这里插入图片描述
(这里初始数据一共是10个,第一次1个队列和1个队列融合,第二次2个数据的队列和2个数据的队列融合,第三次4个数据的队列和4个数据的队列融合,第四次8个数据的队列和8个数据(实际上其中只有2个数据)的队列融合),从而看出融合次数的规律:
在这里插入图片描述
【设 i 为运行次数,当( i * 2 ) < 总数据个数时继续排序】

2.具体合并方法

(1).需要设置一个临时辅助空间 brr 来存放两两队列对比排序完数据。
(2).左右手对比时,先从左右手中各从左到右对比,两手中较小的那个放到辅助空间 brr 中,循环这样比较直到一只手空了,则剩下的那个手里的数据依次放到辅助空间 brr 中。
(3).两手放完后,再去抓另外两组

*设置临时辅助空间 brr 的原因
例如数组arr[]={7,9,11,12,12,11,88,98,66,68}时:
在这里插入图片描述
当只用arr时会导致无序,不稳定,所以需要设立临时辅助空间brr。

3.代码实现

<1>.所需头文件和宏替换:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<stdbool.h>

#define ar arr[]={21,12,7,9,11,98,56,88,66,68}
#define ELEM_TYPE int

int ar;

<2>.顺序表的建立:

typedef struct Sqlist
{
	ELEM_TYPE* data;
	int length;
	int SIZE;
}Sqlist,*PSqlist;

<3>.顺序表的初始化:

void Init_Sqlist(PSqlist L)
{
	L->data = arr;
	L->length = 0;
	L->SIZE = sizeof(arr) / sizeof(arr[0]);
}

<4>.打印

void Show(PSqlist L)
{
	for (int i = L->length; i < L->SIZE; i++)
	{
		printf("%d ", L->data[i]);
	}
	printf("\n\n");
}

<5>.每一次的归并排序:

void Merge(PSqlist L,int gap)          //控制一次融合,gap是每次融合组中数据的个数
{
	ELEM_TYPE* brr = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * L->SIZE);   //申请额外辅助空间brr
	int i = 0;          //辅助空间的下标

	//左右手的边界
	int low1 = 0;                           
	int high1 = low1 + gap - 1;      //有边界=左边界+偏移量
	int low2 = high1 + 1;
	int high2 = low2 + gap - 1 < L->SIZE ? low2 + gap - 1 : L->SIZE - 1;

	while (low2 <= high2)              //最外层保证可以抓取值
	{
		while (low1 <= high1 && low2 <= high2)        //内层保证两只手都有值可以比较大小
		{
			if (arr[low1] <= arr[low2])
			{
				brr[i++] = arr[low1++];
			}
			else
			{
				brr[i++] = arr[low2++];
			}
		}
		//内层循环退出的时候代表有一只手为空,这时直接将不为空的手里的数据依次放到brr中

		while (low1<=high1)         //左手不空时
		{
			brr[i++] = arr[low1++];
		}

		while (low2<=high2)         //右手不空时
		{
			brr[i++] = arr[low2++];
		}

		//已经将这次的左右手都放到brr中了,就让左右手再去抓另外的组
		low1 = high2 + 1;
		high1 = low1 + gap - 1;
		low2 = high1 + 1;
		high2 = low2 + gap - 1 < L->SIZE ? low2 + gap - 1:L->SIZE - 1;    //这里是决定外层循环退出的关键

	}
	//外层循环退出代表两种情况:1.此时全部数据都已经排完,目前左右手都是空的,这种情况不用管
	                          //2.此时左手有值右手没有值,这时只需要将左手的值依次放进brr就可以

	while (low1 < L->SIZE)
	{
		brr[i++] = arr[low1++];
	}

	//此时brr已经满了,只需将brr都覆盖arr即可
	for (int k = 0; k < L->SIZE; k++)
	{
		arr[k] = brr[k];
	}

	//最后将临时辅助空间释放防止内存泄露
	free(brr);
}

<6>.二路归并排序:

void MergeSort(PSqlist L)          //控制总的融合次数
{
	for (int i = L->length + 1; i < L->SIZE; i = i * 2)
	{
		Merge(L, i);
	}
}

<7>.主函数:

int main()
{
	Sqlist head;
	Init_Sqlist(&head);
	printf("初始数据为:");
	Show(&head);

	MergeSort(&head);
	printf("二路归并排序后数据为:");
	Show(&head);
	return 0;
}

<8>.运行结果:
在这里插入图片描述
希望这篇博客能帮助大家更好的学习和理解二路归并排序
感谢阅读!

  • 7
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是使用C语言实现二路归并排序算法的示例: ```c #include <stdio.h> // 归并函数 void merge(int arr[], int left[], int leftSize, int right[], int rightSize) { int i = 0, j = 0, k = 0; while (i < leftSize && j < rightSize) { if (left[i] <= right[j]) { arr[k++] = left[i++]; } else { arr[k++] = right[j++]; } } while (i < leftSize) { arr[k++] = left[i++]; } while (j < rightSize) { arr[k++] = right[j++]; } } // 二路归并排序函数 void mergeSort(int arr[], int size) { if (size < 2) { return; } int mid = size / 2; int left[mid]; int right[size - mid]; // 将数组拆分为两个子数组 for (int i = 0; i < mid; i++) { left[i] = arr[i]; } for (int i = mid; i < size; i++) { right[i - mid] = arr[i]; } // 递归地对子数组进行排序 mergeSort(left, mid); mergeSort(right, size - mid); // 归并两个子数组 merge(arr, left, mid, right, size - mid); } int main() { int arr[] = {8, 2, 5, 3, 9, 1}; int size = sizeof(arr) / sizeof(arr[0]); printf("原始数组:"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); mergeSort(arr, size); printf("排序后的数组:"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; } ``` 在这段代码中,我们定义了两个函数:`merge`和`mergeSort`。`merge`函数用于将两个有序的子数组合并成一个有序的数组。`mergeSort`函数使用递归的方式对数组进行二路归并排序。 在`main`函数中,我们创建一个包含一些元素的数组,并调用`mergeSort`函数对数组进行排序。最后,我们打印排序后的数组。 这段代码实现了二路归并排序算法,将数组分割成多个子数组,然后通过归并操作将子数组合并成一个有序的数组,最终实现整个数组的排序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逐梦的白鹰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值