【数据结构与算法】归并排序详解:归并排序算法,归并排序非递归实现

一、归并排序

归并排序是一种经典的排序算法,它使用了分治法的思想。下面是归并排序的算法思想:

  1. 递归地将数组划分成较小的子数组,直到每个子数组的长度为1或者0。
  2. 将相邻的子数组合并,形成更大的已排序的数组,直到最终得到一个完全排序的数组。

归并排序的过程可以分为三个步骤:拆分(Divide)、合并(Merge)和排序(Sort)。

  1. 拆分:将待排序的数组不断地划分为两个子数组,直到每个子数组的长度为1或者0。
  2. 合并:将相邻的子数组合并为一个较大的已排序数组,通过比较两个子数组的首元素,按照从小到大的顺序逐个将元素放入一个辅助数组。
  3. 排序:重复进行合并的过程,直到最终得到完全排序的数组。

归并排序的时间复杂度为O(nlogn),其中n是待排序数组的长度。空间复杂度为O(n),主要是由于需要使用一个大小与原始数组相同的辅助数组来存储合并的结果。

归并排序是一种稳定的排序算法,即相等元素的相对顺序在排序前后保持不变。在合并的过程中,如果遇到两个相等的元素,我们会先将来自前一个子数组的元素放入辅助数组,这样可以确保相等元素的相对顺序不会改变。

代码实现:

// 归并排序具体功能实现函数
void MergeSortFun(int* a, int* temp, int begin, int end)
{// 如果数组大小为1或者空,直接返回上一层if (begin >= end){return;}// 划分数组,递归调用 MergeSortFun 对左右子数组进行排序int mid = (begin + end) / 2;MergeSortFun(a, temp, begin, mid);MergeSortFun(a, temp, mid + 1, end);// 合并两个有序子数组int begin1 = begin;int end1 = mid;int begin2 = mid + 1;int end2 = end;int i = begin;// 依次比较两个子数组的元素,将较小的元素放入辅助数组 temp 中while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){temp[i++] = a[begin1++];}else {temp[i++] = a[begin2++];    }}// 将剩余的元素放入辅助数组 temp 中while (begin1 <= end1){temp[i++] = a[begin1++];}while (begin2 <= end2){temp[i++] = a[begin2++];}// 将辅助数组 temp 中的元素拷贝回原数组for (i = begin; i <= end; i++){a[i] = temp[i];}
}// 归并排序入口函数
void MergeSort(int* a, int n)
{int begin = 0;int end = n - 1;// 创建大小为 n 的辅助数组 tempint* temp = (int*)malloc(sizeof(int) * n);// 调用 MergeSortFun 对数组 a 进行归并排序MergeSortFun(a, temp, begin, end);// 释放辅助数组 temp 的内存空间free(temp);
}

二、归并排序非递归实现 

归并排序可以使用非递归的方式实现,其算法思想如下:

  1. 将待排序的数组划分为多个大小为1的子数组。
  2. 分别对这些子数组进行两两合并,形成新的有序子数组。
  3. 不断重复步骤2,直到得到一个有序的数组。

具体的非递归实现过程如下:

  1. 首先,定义一个大小为n的辅助数组temp用于存储合并后的有序子数组。
  2. 设置一个变量gap初始值为1,表示每次合并的两个子数组的大小。
  3. 进行多轮合并,直到gap大于等于n。
    • 在每一轮合并中,将数组分为多个大小为gap的子数组,将相邻的两个子数组合并为一个有序子数组。合并时,使用双指针i和j分别指向两个子数组的起始位置,比较两个子数组对应位置上的元素大小,较小的元素放入temp数组中,同时移动指针,直到一个子数组遍历完成。将未遍历完的子数组中剩余的元素直接放入temp数组中。
    • 更新gap的值为2倍,继续下一轮合并。
  4. 最后一轮合并时,gap可能大于n,因此需要额外的判断和处理。
  5. 将temp数组中的元素拷贝回原数组中。

通过不断调整gap的大小,将待排序数组进行分组和合并操作,直到得到一个完全有序的数组。非递归实现的归并排序避免了递归带来的额外开销,提高了算法的效率。、

 代码实现:

void mergesortnr(int* a, int* temp, int begin, int mid, int end)
{// 定义指针和索引int head1 = begin;int tail1 = mid;int head2 = mid + 1;int tail2 = end;int i = begin;// 合并两个有序子数组// [head1,tail1] 和 [head2,tail2] 归并while (head1 <= tail1 && head2 <= tail2){// 比较两个子数组对应位置上的元素大小,较小的元素放入temp数组中if (a[head1] < a[head2]){temp[i++] = a[head1++];}else{temp[i++] = a[head2++];}}// 将第一个子数组中剩余的元素放入temp数组中while (head1 <= tail1){temp[i++] = a[head1++];}// 将第二个子数组中剩余的元素放入temp数组中while (head2 <= tail2){temp[i++] = a[head2++];}// 将temp数组中的元素拷贝回原数组中memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));
}void MergeSortNR(int *a, int n) 
{// 创建辅助数组int* temp = (int*)malloc(sizeof(int) * n);int gap = 1;// 不断调整gap的大小,分组合并for (gap = 1; gap < n; gap *= 2){// 对每一组进行合并for (int i = 0; i < n - gap; i += 2 * gap){// 计算子数组的起始索引、中间索引和结束索引int begin = i;、
/*如果i + 2 * gap - 1大于等于数组长度n,说明当前的子数组已经超出了数组的范围,此时将结束索引end赋值为n - 1,即最后一个元素的索引。如果i + 2 * gap - 1小于数组长度n,说明当前的子数组还在数组的范围内,此时将结束索引end赋值为i + 2 * gap - 1。*/int end = i + 2 * gap - 1 >= n ? n - 1 : i + 2 * gap - 1;int mid = i + gap - 1;// 调用mergesortnr函数合并子数组mergesortnr(a, temp, begin, mid, end);}}
}

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

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

相关文章

OpenCV读取摄像头窗口变大且很卡的解决方法

视频讲解 OpenCV读取摄像头窗口变大且很卡的解决方法 测试过程 读取摄像头窗口变大且很卡的代码 import cv2 cap cv2.VideoCapture(0) if not cap.isOpened():print("Cannot open camera")exit() while True:ret, frame cap.read()if not ret:print("no str…

Arduino开发实例-SDS011粉尘检测传感器驱动

SDS011粉尘检测传感器驱动 文章目录 SDS011粉尘检测传感器驱动1、SDS011介绍2、硬件准备及接线3、代码实现在本文中,将介绍如何使用 Arduino 动粉尘传感器 SDS011 制作空气质量监测系统。 1、SDS011介绍 粉尘本身根据它们的大小分为两类。 直径在2.5至10微米之间的称为粗颗粒…

conda install命令无法安装pytorch

由于网络问题&#xff0c;直接采用conda install命令可能无法直接下载对应的cuda包。 方法一&#xff1a;采用pip命令替代 步骤1&#xff1a;切换pip的源为国内源&#xff1a; 若只是临时切换&#xff1a; pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-p…

SpringMVC-.xml的配置

文章目录 一、对pom.xml的配置二、对web.xml1.第一种方式2. 第二种方式 三、对SpringMVC.xml的配置 一、对pom.xml的配置 <!-- 打包成war包--><packaging>war</packaging> <dependencies><!-- SpringMVC--><dependency><gro…

Minio文件分片上传实现

资源准备 MacM1Pro 安装Parallels19.1.0请参考 https://blog.csdn.net/qq_41594280/article/details/135420241 MacM1Pro Parallels安装CentOS7.9请参考 https://blog.csdn.net/qq_41594280/article/details/135420461 部署Minio和整合SpringBoot请参考 https://blog.csdn.net/…

k8s-ingress一

Comfigmap&#xff1a;存储数据 Date&#xff1a; Key&#xff1a;value 挂载的方式&#xff0c;把配置信息传给容器 生产当中的yml文件很长&#xff1a; 有deployment 容器的探针 资源限制 Configmap 存储卷 Service Ingress K8s的对外服务&#xff0c;ingress Se…

【Linux学习】进程信号

目录 十七.进程信号 导言 17.1 linux中的信号列表 17.2 标准信号与实时信号 17.3 信号的产生 17.3.1 通过终端按键产生信号 17.3.2 调用系统函数产生信号 17.3.3 软件条件产生信号 17.3.4 硬件异常产生信号 17.3.5 【补充】核心转储 Core Dump 17.4 信号的阻塞 17.4.1 信号相关…

将字符串中可能被视为正则表达式的特殊字符进行转义re.escape()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将字符串中可能被视为 正则表达式的特殊字符 进行转义 re.escape() [太阳]选择题 请问以下代码最后输出的结果是&#xff1f; import re s [a-z] print("【显示】s ",s) print(&q…

python24.1.21面向对象编程

面向对象编程&#xff1a;创建对象&#xff0c;定义对象的方法和属性 封装&#xff1a;隐藏内部实现细节&#xff0c;只通过外部接口访问使用 继承&#xff1a;允许创建有层次的类&#xff08;子类&#xff0c;父类&#xff09; 多态&#xff1a;同样接口&#xff0c;对象具体…

JVM:Java类加载机制

Java类加载机制的全过程&#xff1a; 加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的&#xff0c;类型的加载过程必须按照这种顺序按部就班地开始&#xff0c;而解析阶段则不一定&#xff1a;它在某些情况下可以在初始化阶段之后再开始&#xff0c; 这是为了支持Java…

QWidget: Must construct a QApplication before a QWidget 20:10:25: 程序异常结束。

如果你在Windows上混合并匹配了Release和Debug的dll&#xff0c;则会导致这种情况。我的链接的库是release的版本&#xff0c;也就是qwt.dll&#xff0c;但是点击Qt Creator的运行按钮默认是debug启动&#xff0c;所以报错了&#xff0c;Qt Creator运行按钮里选择release就可以…

重构改善既有代码的设计-学习(一):封装

1、封装记录&#xff08;Encapsulate Record&#xff09; 一些记录性结构&#xff08;例如hash、map、hashmap、dictionary等&#xff09;&#xff0c;一条记录上持有什么字段往往不够直观。如果其使用范围比较宽&#xff0c;这个问题往往会造成许多困扰。所以&#xff0c;记录…

【Linux系统编程二十八】基于条件变量的阻塞队列(生产消费模型)

【Linux系统编程二十八】基于条件变量的阻塞队列(生产消费模型&#xff09; 一.同步问题二.条件变量1.实现原理2.等待的前提3.使用接口①.【定义条件变量】②.【初始化条件变量】③.【让线程去条件变量下等待】④.【为什么第二个参数是锁&#xff1f;】条件变量和锁的关系是什么…

【开源】基于JAVA语言的软件学院思政案例库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统管理员2.2 普通教师 三、系统展示四、核心代码4.1 查询思政案例4.2 审核思政案例4.3 查询思政课程4.4 思政案例点赞4.5 新增思政案例评语 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的软件学…

Java根据模板文件生成excel文件,同时将excel文件转换成图片

需求 需要将指定数据导出成表格样式的图片&#xff0c;如图 业务拆解 定义一个导出模板将得到的数据填入模板中&#xff0c;生成excel文件将ecxel文件转换成png格式的图片 代码实现 需要引入的依赖 <dependency><groupId>cn.hutool</groupId><artif…

【JavaEE】_基于UDP实现网络通信

目录 1. 服务器 1.1 实现逻辑 1.2 代码 1.3 部分代码解释 2. 客户端 2.1 实现逻辑 2.2 代码 2.3 客户端部分代码解释 3. 程序运行结果 4. 服务器客户端交互逻辑 此篇内容为实现UDP版本的回显服务器echo server&#xff1b; 普通服务器&#xff1a;收到请求&#xff…

Docker搭建MySQL主从数据库-亲测有效

1、测试环境概述 1、使用MySQL5.7.35版本 2、使用Centos7操作系统 3、使用Docker20版本 案例中描述了整个测试的详细过程 2、安装Docker 2.1、如果已经安装docker,可以先卸载 yum remove -y docker \ docker-client \ docker-client-latest \ docker-common \ docker-l…

机器人电机综述 — 电机分类、舵机、步进与伺服、物理性质和伺服控制系统

电机综述 图片与部分素材来自知乎大佬不看后悔&#xff01;最全的电机分类&#xff0c;看这一篇就够了&#xff01; - 知乎 (zhihu.com)&#xff0c;本文只是把机器人中常用的电机知识提炼了一下 1 按照结构和工作原理划分 1. 同步电机 ​ 电机的转速与定子磁场的转速相同步…

线性表的案例引入 | 稀疏多项式的运算

#include <iostream> using namespace std;#define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2typedef int Status;// 定义单链表 typedef struct PNode {float coef; //系数int expn; //指数struct PNode *nex…

基于深度学习的细胞感染性识别与判定

基于深度学习的细胞感染性识别与判定 基于深度学习的细胞感染性识别与判定引言项目背景项目意义项目实施数据采集与预处理模型选择与训练模型评估与优化 结果与展望结论 基于深度学习的细胞感染性识别与判定 引言 随着深度学习技术的不断发展&#xff0c;其在医学图像处理领域…