【数据结构】归并排序 —— 递归及非递归解决归并排序

归并排序

  • 一、归并排序
    • 1、归并排序的思想
    • 2、归并排序代码实现(递归)
      • <1> 归并排序的递归区间
      • <2> 归并排序的稳定性
      • <3> 拷贝
    • 3、归并排序代码实现(非递归)
      • <1> 循环区间溢出问题
  • 二、总结

一、归并排序

1、归并排序的思想

假设我们要排一个升序的数组
先看下面的动图
在这里插入图片描述

假设有两组升序数据
我们设置 ptr1 和 ptr2 来代表数组下标
将两数据进行比较,将小的填入一个新的数组
当两个数组中的数都进入新的数组
接下来我们只需要将该数组复制到原数组中
就完成了排序

如果我们要排一个升序数组
就要将它分成两个小的升序数组

若想得到两个小的升序数组
就要将每一个小的升序数组分成两个更小的升序数组

。。。。。。

————————————————————————————
于是我们想到了用递归的方式
一层层递归
直到只剩下一个不用数据,然后返回进行排序

2、归并排序代码实现(递归)

void _MergeSort(int* a, int* tmp, int left, int right)
{//返回停止条件if (left == right){return;}//取中间值int mid = (left + right) / 2;//递归//[left, mid][mid + 1, right]//递归左边_MergeSort(a, tmp, left, mid);//递归右边_MergeSort(a, tmp, mid + 1, right);//进行单次归并int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;//记录起始位置int begin = left, end = right;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[begin++] = a[begin1++];}else {tmp[begin++] = a[begin2++];}}while (begin1 <= end1){tmp[begin++] = a[begin1++];}while (begin2 <= end2){tmp[begin++] = a[begin2++];}//进行拷贝memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));}//归并排序
void MergeSort(int* a, int left, int right)
{//开辟一个复制数组int* tmp = (int*)malloc(sizeof(int) * (right - left + 1));if (tmp == NULL){perror("malloc fail:");exit(1);}//进行归并排序_MergeSort(a, tmp, left, right);//数组销毁free(tmp);tmp = NULL;
}

<1> 归并排序的递归区间

递归区间1
[left, mid][mid + 1, right]
递归区间2
[left, mid - 1][mid, right]

看这两个区间有所不同
上面代码我采用的是区间1
而没有采用区间2
因为区间2会出现死循环,导致栈溢出
看下面的图:

区间1:
在这里插入图片描述
区间2:
在这里插入图片描述
同样的 mid 但是当递归至只剩下两个时,区间2就会陷入死循环

<2> 归并排序的稳定性

while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[begin++] = a[begin1++];}else {tmp[begin++] = a[begin2++];}}

在比较 begin1 和 begin2 时我们用的是 " <= "
这样前面的数就会在前面
在这里插入图片描述
在图上我们发现我们在面对相同的数时,我们会先将 ptr1 中的数填入数组,那么相同数的相对位置没有发生改变,我们说归并排序是稳定的

<3> 拷贝

//进行拷贝
memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));

我们是边排序边拷贝的
如果等到最后拷贝会有出问题的风险
memcpy函数

3、归并排序代码实现(非递归)

如果是用非递归的方式
我们可以用循环
设置gap变量,gap 每次循环结束 gap *= 2
完成整个数组的归并
在这里插入图片描述

代码实现:

//归并排序(非递归)
void MergeSortNonR(int* a, int left, int right)
{//开辟复制数组int* tmp = (int*)malloc(sizeof(int) * (right - left + 1));if (tmp == NULL){perror("malloc fail:");exit(1);}int gap = 1;//进行单次排序while (gap < right + 1){for (int i = 0; i < right + 1; i += gap * 2){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + gap * 2 - 1;int begin = i;printf("[%d, %d][%d, %d]", begin1, end1, begin2, end2);//当begin2超过数组区间if (begin2 > right){break;}//当只有end2超过区间if (end2 > right){end2 = right;}//单次归并while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[begin++] = a[begin1++];}else{tmp[begin++] = a[begin2++];}}while (begin1 <= end1){tmp[begin++] = a[begin1++];}while (begin2 <= end2){tmp[begin++] = a[begin2++];}//将数组进行复制memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}printf("\n\n");gap *= 2;}//销毁复制数组free(tmp);tmp = NULL;
}

<1> 循环区间溢出问题

当begin2超过数组区间
if (begin2 > right)
{break;
}
当只有end2超过区间
if (end2 > right)
{end2 = right;
}

在循环过程中,除了 begin1 不会超出数组区间
其他三个都有可能超出区间
我们将所有区间都打印出来
在这里插入图片描述
我们发现超出区间总共分为三种情况
在这里插入图片描述
当 begin2 和 end1 超出区间时:
说明后面的整个区间都超出了数组的范围
那就不用归并

当 end2 超出区间时:
说明后面的一部分数没有超出区间
可以将 end2 改为区间的最大值,继续进行归并

二、总结

归并排序的时间复杂度为 O(N * logN)
是比较快的排序
但是归并排序有它的缺陷
它的空间复杂度为 O(N)
排序需要开辟空间
当排序数量过大时有风险

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/477050.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

调大Vscode资源管理器字体

对于调整资源管理器字体大小&#xff08;也就是下图红框&#xff09;&#xff0c;查找了网上很多方法。要么介绍的方法是调整了代码字体&#xff0c;要么是调节了终端字体&#xff0c;要么是通过整体放缩实现的调整&#xff0c;总之都不合适。 唯一的调整方法是在几篇CSDN里看到…

【Linux】-学习笔记04

第十二章、磁盘管理 1.查看磁盘空间使用量 1.1df命令 作用&#xff1a; 列出文件系统的磁盘空间占用情况 df&#xff0c;disk free&#xff0c;通过文件系统来快速获取空间大小的信息&#xff0c;当我们删除一个文件的时候&#xff0c;这个文件 不是马上就在文件系统当中消…

centos 服务器 docker 使用代理

宿主机使用代理 在宿主机的全局配置文件中添加代理信息 vim /etc/profile export http_proxyhttp://127.0.0.1:7897 export https_proxyhttp://127.0.0.1:7897 export no_proxy"localhost,127.0.0.1,::1,172.171.0.0" docker 命令使用代理 例如我想在使用使用 do…

Vue中Select选择器el-option实现动态多选

效果如图&#xff1a; 前端列表块显示部分&#xff1a; <el-table :data"tableData" border stripe :header-cell-class-name"headerBg" selection-change"handleSelectionChange"><el-table-column type"selection" width…

【ubuntu24.04.1最简洁安装方案】

我的电脑配置&#xff1a; 128GB固态硬盘&#xff0c;1TB 机械硬盘&#xff0c;我把整个 windows 系统全噶掉了&#xff0c;只安装ubuntu24.04.1一个Linux系统噶windows系统&#xff0c; 推荐使用 DiskGenius这个工具&#xff0c;好用&#xff0c;但是也要弄明白了再用啊&#…

k8s集群加入node节点为ubuntu 22.04

文章目录 1.环境准备1.1 关闭无用服务1.2 环境和网络1.3 apt源1.4 系统优化 2. 装containerd3. 接入k8s集群3.1 kubelet、kubeadm、kubectl安装3.2 缺少一个镜像3.3 接入k8s集群 4. 一些相关问题 1.环境准备 rootcto-gpu-pro-n01:~# lsb_release -a No LSB modules are availa…

C#桌面应用制作计算器进阶版01

基于C#桌面应用制作计算器做出了少量改动&#xff0c;其主要改动为新增加了一个label控件&#xff0c;使其每一步运算结果由label2展示出来&#xff0c;而当点击“”时&#xff0c;最终运算结果将由label1展示出来&#xff0c;此时label清空。 修改后运行效果 修改后全篇代码 …

如何构建高效的接口自动化测试框架?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在选择接口测试自动化框架时&#xff0c;需要根据团队的技术栈和项目需求来综合考虑。对于测试团队来说&#xff0c;使用Python相关的测试框架更为便捷。无论选…

数据结构-8.Java. 七大排序算法(上篇)

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 上篇主要实现 前四种排序算法: 直接插入, 希尔, 选择, 堆排。 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 …

算法日记 32 day 动态规划(完全背包)

同样是背包问题&#xff0c;但01背包和完全背包是两个类型的问题。 完全背包&#xff1a; 完全背包与01背包的区别在于物品的个数是否是无限的。除此之外&#xff0c;在解决01背包的时候dp的背包遍历的顺利是倒序&#xff0c;为的是保证物品只被添加一次&#xff0c;而完全背包…

数据结构之树与二叉树

华子目录 1.树和二叉树的定义1.1树的定义1.2树的基本术语1.3线性结构和树结构1.4二叉树的定义 2.二叉树的性质和存储结构2.1二叉树的性质2.2二叉树的存储结构2.2.1顺序存储2.2.2链式存储 2.3遍历二叉树2.4大作业&#xff1a;二叉树的基本操作2.4.1代码思路&#xff08;仅供参考…

MYSQL——多表设计以及数据库中三种关系模型

大致介绍数据库中三种关系模型 一对多&#xff08;1:N&#xff09; 定义&#xff1a; 一个实体可以与另一个实体的多个实例相关联&#xff0c;而后者只能与前者的一个实例相关联。 例子&#xff1a; 学生和课程的关系。 学生&#xff08;1&#xff09;&#xff1a;每个学生…

企业网页设计的安全与数据保护

企业网页设计不仅要考虑美观和功能性&#xff0c;安全与数据保护也是重中之重。在这个信息爆炸的时代&#xff0c;用户的数据隐私和安全问题日益凸显&#xff0c;企业必须采取多种措施来保障用户的信息安全。 首先&#xff0c;**SSL加密**是基础中的基础。通过使用SSL证书&…

观察者模式和订阅模式

观察者模式和订阅模式在概念上是相似的&#xff0c;它们都涉及到一个对象&#xff08;通常称为“主题”或“发布者”&#xff09;和多个依赖对象&#xff08;称为“观察者”或“订阅者”&#xff09;之间的关系。然而&#xff0c;尽管它们有相似之处&#xff0c;但在某些方面也…

logback动态获取nacos配置

文章目录 前言一、整体思路二、使用bootstrap.yml三、增加环境变量四、pom文件五、logback-spring.xml更改总结 前言 主要是logback动态获取nacos的配置信息,结尾完整代码 项目springcloudnacosplumelog&#xff0c;使用的时候、特别是部署的时候&#xff0c;需要改环境&#…

工具学习_Docker

0. Docker 简介 Docker 是一个开源平台&#xff0c;旨在帮助开发者构建、运行和交付应用程序。它通过容器化技术将应用程序及其所有依赖项打包在一个标准化的单元&#xff08;即容器&#xff09;中&#xff0c;使得应用程序在任何环境中都能保持一致的运行效果。Docker 提供了…

基础知识学习上

基础知识学习上 1.关于print1.1 format 方法 2.运算符2.1 除法运算2.2 幂运算 3.条件控制语句3.1 if语句3.2 循环语句 4.复杂数据类型4.1列表4.2字典4.3字符串 5.函数 1.关于print 分隔符 print(1, 2, 3, 4, sep-) print(1, 2, 3, 4, sep。)结尾符 print(1, 2, 3, 4, end?) pr…

无监督跨域目标检测的语义一致性知识转移

Semantic consistency knowledge transfer for unsupervised cross domain object detection 无监督跨域目标检测的语义一致性知识转移 作者: Zichong Chen, Ziying Xia, Xiaochen Li, Junhao Shi, Nyima Tashi, Jian Cheng 所属机构: 电子科技大学信息与通信工程学院&…

AI智能稿件排版系统订单管理系统

在现代制造业和服务行业中&#xff0c;高效的生产流程和精确的订单管理是企业保持竞争优势的核心要素。AI智能稿件排版系统和订单管理系统作为一体化解决方案&#xff0c;以其强大的自动化能力和智能化技术&#xff0c;帮助企业实现排版效率提升、数据格式兼容性增强和生产流程…

Android Google登录接入

官方文献&#xff1a; 1、前期准备&#xff1a; https://developers.google.cn/identity/sign-in/android/legacy-start-integrating?hlzh-cnhttps://developers.google.cn/identity/sign-in/android/legacy-start-integrating?hlzh-cn 2、具体开发&#xff1a; 新版 Googl…