【排序】归并排序(递归+非递归图示详解哦)

news/2025/2/12 4:00:40/

全文目录

  • 引言
  • 归并排序
    • 思路
    • 递归实现
  • 归排非递归
    • 思路
    • 实现
  • 总结

引言

在本篇文章中,将继续介绍一种排序算法:归并排序。
归并排序运用了归并的思想,即将两个有序数列归并为一个有序数列。在前面的合并两个有序链表时,运用了这种思想:戳我看归并实现合并两个有序链表(方法二)

归并排序

思路

归并排序需要一块与数组大小相同的空间,用于临时存储归并后的数据;

排序时,先将整个数组平分为两份,再分为4份……以此类推,直到不能再平分后(区间只剩一个元素);
向上归并,即将两个区间归并到临时空间中的对应位置;
再将临时数组中对应区间中已经排序好的数据拷回原数组;
一直向上归并,直到排序完成。
(需要注意的是,这里的平分不是同时进行的,在递归中,是一条线一条线进行的。左边递归到头后,返回给上一级,继续递归倒数第二层的右边,右边到底返回到上一级,执行最底层左右区间的归并。归并结束后返回到倒数第三层,然后递归该层的右边……)

在这里插入图片描述

递归实现

与快排的递归模式不同,快排是对最大的区间操作完成后,向下递归,对左右区间进行操作,直到排序结束;而归排是先递归到最小区间,再向上归并。所以在实现快排时,每层的操作在前,函数递归在后;而快排是递归在前,每层的操作在后;

函数有4个参数:原数组、区间的左右下标、临时空间。
初始传给函数的参数为整个数组,向下递归,当begin>=end时,return结束递归;
创建midi,记录区间的中间位置,再将左右区间分别递归;

之后实现对区间的操作:
首先用 begin1 、end1 、begin2 、end2记录要归并区间的左右区间的始末下标,并用k记录要归并的区间在临时空间temp中的对应起始位置;
然后将左右区间归并到temp的对应位置;
最后将temp对应空间中的数据拷贝回来:

在这里插入图片描述

对于归并的过程,即将两个区间中的数据依次比较,将较小的元素尾插到临时空间中:

在这里插入图片描述

void _MergeSort(int* a, int begin, int end, int* temp)
{if (begin >= end){return;}int midi = (begin + end) / 2;_MergeSort(a, begin, midi, temp);_MergeSort(a, midi+1, end, temp);int begin1 = begin, end1 = midi;int begin2 = midi+1, end2 = end;int k = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){temp[k++] = a[begin1++];}else{temp[k++] = a[begin2++];}}while (begin1 <= end1){temp[k++] = a[begin1++];      }while (begin2 <= end2){temp[k++] = a[begin2++];}memcpy(a + begin, temp + begin, sizeof(int)*(end - begin + 1));
}void MergeSort(int* a, int n) //递归实现
{int left = 0;int right = n - 1;int* temp = (int*)malloc(sizeof(int) * n);if (temp == NULL){perror("malloc");return;}_MergeSort(a, left, right, temp);free(temp);
}

归排非递归

思路

非递归实现归排时,与快排不同,不用记录上一层的区间,将某个区间归并后,转回原数组即可。所以我们只需要将数组分组,归并每两组的值,每组元素的数量从1开始,每次循环后,每组元素个数乘2,直到大于数组长度循环结束;

每层循环内,需要将这一层中,两个为一组的区间全部归并完成,并将这些数据转回原数组中:

(图)

实现

实现非递归时,首先动态开辟一块临时空间temp;
然后,创建gap为每层中每个需要归并区间的元素个数,并初始化为1;

外层while循环控制gap的值,大于等于数组的元素个数时,循环结束;
内层for循环控制每层中要归并的分组(两个元素个数为gap的小区间为一组归并):
左区间的起始位置begin1为i,结束位置end1为begin1+gap-1;右区间的起始位置begin2为end1+1,结束位置end2为begin2+gap-1;k为对应在temp中的起始位置下标;
然后需要判断的是,这组区间是否越界。由于for循环只是控制了i<n,即begin1<n,所以end1、begin2、end2都有可能越界;
当end1或begin2越界时,说明这组(左右)区间已经少了一个区间,不用归并了。当end2越界时,说明一组左右区间是存在的,需要归并,将end2改为数组最后一个元素的下标即可;

然后对左右区间进行归并,归并到temp中的对应位置;
每组(左右)区间归并后,就将数组拷回原数组:
【需要注意的是,由于之前的end2是可能越界的,所以不能直接拷贝2*gap个数据回去,而应该为end2-i+1个数据】

在这里插入图片描述

void MergeSortNonR2(int* a, int n) //非递归实现(分别转移)
{int gap = 1;int* temp = (int*)malloc(sizeof(int) * n);if (temp == NULL){perror("malloc");return;}while (gap < n){for (int i = 0; i < n; i += (gap * 2))//每层{int begin1 = i, end1 = begin1 + gap - 1;int begin2 = end1 + 1, end2 = begin2 + gap - 1;int k = begin1;//判断是否越界(如果越界,直接break跳过归并)if (end1 >= n || begin2 >= n){break;}else if (end2 >= n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){temp[k++] = a[begin1++];}else{temp[k++] = a[begin2++];}}while (begin1 <= end1){temp[k++] = a[begin1++];}while (begin2 <= end2){temp[k++] = a[begin2++];}memcpy(a + i, temp + i, (end2 - i + 1) * sizeof(int));//每次归并都转移}gap *= 2;//走向下一层}
}

总结

到此,关于归并排序的内容就已经介绍完了,同时排序算法的讲解也暂告一段落了
如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦


http://www.ppmy.cn/news/57369.html

相关文章

【华为OD机试真题】超级玛丽通过吊桥的走法(C++Javapython)100%通过率 超详细代码注释 代码解读

超级玛丽通过吊桥的走法 题目描述 超级玛丽好不容易来到新的一关,有一个长长的吊桥,吊桥的尽头是下水管道,其中随机的木板存在缺失,旦踩到就会死亡,死亡后如果还有剩余的生命将在原地复活且不受木板缺失影响,会消耗一次生命,如果跨过了管道,将跌入悬崖,通关失败。 …

Javaweb介绍

Javaweb JavaWeb是一种通过使用Java技术进行Web应用程序开发的方式。Java Web应用程序通常由动态生成的网页组成&#xff0c;与静态的HTML页面不同。 JavaWeb应用程序可以用于各种类型的应用程序&#xff0c;包括电子商务、博客、内容管理系统等。 什么是web容器 Web容器是在…

Python基础合集 练习22 (错误与异常处理语句2)

‘’’ try: 语句块 except: 语句块2 else ‘’’ class Mobe1(): def init(self) -> None: pass def mob1(self):while True:try:num int(input(请输入一个数: ))result 50 / numprint(result)print(50/{0}{1}.format(num, result))except (ZeroDivisionError, ValueEr…

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2

【VQ-VAE-2论文精读】Generating Diverse High-Fidelity Images with VQ-VAE-2 0、前言Abstract1 Introduction2 Background2.1 Vector Quantized Variational AutoEncoder3 Method3.1 Stage 1: Learning Hierarchical Latent Codes3.2 Stage 2: Learning Priors over Latent C…

LeetCode1376. 通知所有员工所需的时间

【LetMeFly】1376.通知所有员工所需的时间 力扣题目链接&#xff1a;https://leetcode.cn/problems/time-needed-to-inform-all-employees/ 公司里有 n 名员工&#xff0c;每个员工的 ID 都是独一无二的&#xff0c;编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。…

使用Sybase sp_recompile重新编译存储过程和触发器

Sybase 15.X中提供了内置的存储过程sp_recompile。该存储过程可令表中的存储过程和触发器在下次使用时重新编译。&#xff08;Causes each stored procedure and trigger that uses the named table to be recompiled the next time it runs.&#xff09; 存储过程和触发器使用…

Mac 地址与 IP 地址有什么区别?

Mac 地址和 IP 地址是两个不同的概念&#xff0c;它们分别代表了计算机网络中的不同层次和地址。Mac 地址是物理地址&#xff0c;是在计算机硬件中存储的地址&#xff0c;通常是以特定的六进制格式表示。每个设备都有一个唯一的 MAC 地址&#xff0c;它可以用来在计算机之间进行…

【华为OD机试 2023最新 】模拟商场优惠打折(C语言题解 100%)

文章目录 题目描述输入描述输出描述用例题目解析代码思路C语言题目描述 模拟商场优惠打折,有三种优惠券可以用,满减券、打折券和无门槛券。 满减券:满100减10,满200减20,满300减30,满400减40,以此类推不限制使用; 打折券:固定折扣92折,且打折之后向下取整,每次购…