基础数据结构之八大排序算法(五)
⑤二路归并排序:
时间复杂度: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>.运行结果:
希望这篇博客能帮助大家更好的学习和理解二路归并排序
感谢阅读!