🔥个人主页:@草莓熊Lotso

🎬作者简介:C++研发方向学习者

📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》

⭐️人生格言:生活是默默的坚持,毅力是永久的享受。

前言:在前面的学习中,我们实现了直接插入排序,希尔排序,直接选择排序,堆排序,冒泡排序,快速排序。我们常见的几种排序算法也差不过都学完了。那么我们这篇博客就继续接着上一篇博客实现完的排序往后写,来讲讲归并排序,还是和之前一样,我们先分部分来讲解,特别声明一下,博客中的排序都是以升序为例的。  


目录

一.递归实现归并排序

具体过程:

代码实现: 

时间复杂度:

二.非递归实现归并排序 

具体过程:

 代码实现:

 三.归并排序的性能测试

代码演示: 


一.递归实现归并排序

 归并排序(MERGE-SORT)是建立在归并操作上的⼀种有效的排序算法,该算法是采用分治法(Divide and Conquer)的⼀个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成⼀个有序表,称为二路归并。

 --我们着重讲一下分治的思想。合并操作需要注意的地方,大家看看后面代码的重点提示就行了

将待排序的数组不断分割成更小的子数组,直到每个子数组只包含一个元素。

具体过程:

  • 找到数组的中间位置,一般是通过 mid = left + (right - left) / 2 来计算,left 和 right 分别表示当前数组范围的起始和结束索引。
  • 然后递归地对数组的左半部分(索引范围从 left 到 mid)和右半部分(索引范围从 mid + 1 到 right)进行同样的分割操作,直到子数组的长度为 1。例如,对于数组 [5, 3, 8, 6, 2],第一次分割得到 [5, 3] 和 [8, 6, 2],然后继续递归分割这两个子数组,直到每个子数组都只有一个元素,即 [5]、[3]、[8]、[6]、[2] 。

代码实现: 

void _MergeSort(int* arr, int left, int right, int* tmp)
{if (left >= right){return;}//分解//[left,mid][mid+1,right]int mid = left + (right-left) / 2;  _MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid+1, right, tmp);//合并int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;//一定是left而不是0,不然会受到影响while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else {tmp[index++] = arr[begin2++];}}//有一个结束了,另外一个还没结束while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//把数据拷贝回去for (int i = left; i <= right; i++){arr[i] = tmp[i];}
}//归并排序--主函数里面不递归,所以可以先不传left和right
void MergeSort(int* arr, int n)
{//开辟一个新数组int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!");exit(1);}_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}

重点提示: 

test.c: 

#include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//归并排序MergeSort(a, size);printf("排序后:");PrintArr(a, size);
}int main()
{test1();return 0;
}

--测试完成,升序排序没有问题,退出码为0

时间复杂度:

  • O(nlogn)

--时间复杂度 = 单次递归时间复杂度 * 递归次数 


二.非递归实现归并排序 

非递归版本的归并排序(迭代实现),核心思想是 “自底向上” 合并子数组,无需递归,直接通过循环控制分组大小(gap),逐步完成排序。

具体过程:

1. 分组合并:

  • gap 从 1 开始,每次翻倍(gap *= 2),表示当前合并的子数组长度。
  • 例如:gap=1 时,合并相邻的 1 个元素 组成的子数组(实际是单个元素,视为有序);gap=2 时,合并相邻的 2 个元素 组成的有序子数组,以此类推。

2. 边界修正

  • 计算 end1,end2 时,需判断是否超出数组范围(n-1 是最后一个元素的索引)。
  • 若 begin2 >= n,说明第二个子数组不存在,直接跳过合并。

3. 合并操作

  • 用双指针法合并两个有序子数组到 tmp,再通过 memcpy 写回原数组。
  • 保证合并后子数组有序,逐层向上归并。

 代码实现:

//非递归实现归并排序
void MergeSortNore(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!");exit(1);}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i,end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;if (begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}int index = i;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else {tmp[index++] = arr[begin2++];}}//有一个结束了,另外一个还没结束while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//把数据拷贝回去memcpy(arr + i, tmp + i, (end2 - i + 1) * sizeof(int));}gap *= 2;}free(tmp);
}

test.c:

#include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//非递归实现归并排序MergeSortNore(a, size);printf("排序后:");PrintArr(a, size);
}int main()
{//TestOP();test1();return 0;
}

--测试完成,升序排序没有问题,退出码为0


 三.归并排序的性能测试

--我们实现完了这么多种排序,我们还是通过测试函数看看它们的性能对比吧

代码演示: 

include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//直接插入排序//InsertSort(a, size);//希尔排序//ShellSort(a, size);//直接选择排序//SelectSort(a, size);//堆排序//HeapSort(a, size);//冒泡排序//BubbleSort(a, size);//快速排序//QuickSort(a, 0, size - 1);//非递归快速排序//QuickSortNoare(a, 0, size - 1);//快速排序进阶版//QuickSortMore(a, 0, size - 1);//归并排序//MergeSort(a, size);//非递归实现归并排序//MergeSortNore(a, size);//非比较排序--计数排序CountSort(a, size);printf("排序后:");PrintArr(a, size);
}// 测试排序的性能对比
void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();SelectSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();MergeSort(a6, N);int end6 = clock();int begin7 = clock();BubbleSort(a7, N);int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{TestOP();//test1();return 0;
}

--我们可以通过下图对比一下各个排序的效率


往期回顾: 

【数据结构初阶】--排序(一):直接插入排序,希尔排序

【数据结构初阶】--排序(二)--直接选择排序,堆排序

【数据结构初阶】--排序(三):冒泡排序,快速排序

结语:到目前为止,我们已经讲将常见的比较类的各种排序都实现完了,在下篇博客中我会为大家介绍一下非比较排序中的计数排序以及总结各种排序的空间复杂度,时间复杂度以及稳定性,如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。

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

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

相关文章

GaussDB 并行创建索引

1 背景当业务数据在单表存储达到一定的数量级时&#xff0c;此时对表创建索引是要花费时间的。GaussDB为了解决这个问题采用并行创建索引技术&#xff0c;以提高创建索引的效率。2 示例步骤1&#xff1a;根据实际情况调整maintenance_work_mem参数该大小。[Rubydtest1 ~]$ gsq…

LOOP Finance:一场 Web3 共和国中的金融制度实验

LOOP Finance 是建构于币安智能链&#xff08;BNB Chain&#xff09;上的定投型DEFI理财协议。 它以凯因斯经济学为启发&#xff0c;设计出一套长期、安全、稳定收益的全新DEFI玩法&#xff0c;兼顾稳健利息回报与DEFI高速成长的潜力。 通过生态机制&#xff0c;LOOP要求每位参…

【golang面试题】Golang递归函数完全指南:从入门到性能优化

引言&#xff1a;递归的本质与挑战 在Golang中&#xff0c;递归函数是一把锋利的双刃剑。它通过函数自身调用实现问题分解&#xff0c;让代码变得简洁优雅&#xff0c;但也容易因无限递归、栈溢出或性能问题让开发者陷入困境。本文将从基础到高级&#xff0c;全面解析Golang递归…

功能安全和网络安全的综合保障流程

摘要网络物理系统是控制机械部件的计算机化系统。这些系统必须既功能安全又网络安全。因此&#xff0c;已建立的功能安全与网络安全标准需求创建网络安全档案&#xff08;ACs&#xff09;&#xff0c;以论证系统是功能安全与网络安全的&#xff0c;即所有功能安全与网络安全目标…

数据科学首战:用机器学习预测世界杯冠军

数据科学首战&#xff1a;用机器学习预测世界杯冠军Scikit-learn实战&#xff1a;从数据清洗到冠军预测的完整指南一、足球预测&#xff1a;数据科学的终极挑战​​世界杯数据价值​​&#xff1a;历史比赛数据&#xff1a;44,000场球队特征指标&#xff1a;200球员数据点&…

一个php 连sqlserver 目标计算机积极拒绝,无法连接问题的解决

一个接口查询数据耗时15秒&#xff0c;还没数据&#xff0c;经查报错日志&#xff1a;SQLSTATE[08001]: [Microsoft][ODBC Driver 17 for SQL Server]TCP 提供程序: 由于目标计算机积极拒绝&#xff0c;无法连接。 命令行执行&#xff1a;netstat -ano | findstr :1433发现结…

生成网站sitemap.xml地图教程

要生成 sitemap.xml 文件&#xff0c;需要通过爬虫程序抓取网站的所有有效链接。以下是完整的解决方案&#xff1a; 步骤 1&#xff1a;安装必要的 Python 库 ounter(line pip install requests beautifulsoup4 lxml 步骤 2&#xff1a;创建 Python 爬虫脚本 (sitemap_genera…

idea拉取新项目第一次启动报内存溢出(java.lang.OutOfMemoryError: Java heap space)

背景&#xff1a; 新拉取一个项目后&#xff0c;第一次启动的时候报错内存溢出&#xff1a; Java 堆内存溢出 (java.lang.OutOfMemoryError: Java heap space) 这个错误表示你的 Java 应用程序需要的内存超过了 JVM 堆内存的分配上限。 解决方案 1.增加堆内存大小 启动应用时添…

安卓雷电模拟器安装frida调试

1.在模拟器中设置调试root和adb 2.在vscode中安装autox.js 3.在github上下载auto.js组件 新地址链接看来大佬的项目也经历了波折https://blog.csdn.net/weixin_41961749/article/details/145669531 github地址https://github.com/aiselp/AutoX/releases 将下载的apk放入雷电…

Godot ------ 初级人物血条制作02

Godot ------ 初级人物血条制作02引言正文血条动态显示引言 在 Godot ------ 初级人物血条制作01 一文中我们介绍了如何构建一个初级血条&#xff0c;但是我们并没有涉及如何动态显示血条。本文我们将介绍如何动态显示血条。 正文 血条动态显示 首先&#xff0c;我们为当前…

(Python)待办事项升级网页版(html)(Python项目)

源代码&#xff1a; app.py from flask import Flask, render_template, request, redirect, url_for, jsonify import json import osapp Flask(__name__)# 数据存储文件 DATA_FILE "todos.json"def load_todos():"""从文件加载待办事项"&q…

智慧养老破局:科技如何让“老有所养”变成“老有优养”?

随着人口老龄化加剧&#xff0c;“养老”成了社会关注的焦点。传统养老往往停留在“有地方住、有人照顾”的基础需求&#xff0c;而智慧养老则通过科技与人文的结合&#xff0c;让老年人的生活从“老有所养”升级到“老有优养”。不仅活得安心&#xff0c;更能活得有尊严、有质…

自学嵌入式 day45 ARM体系架构

一、SOCRAM&#xff1a;随机访问存储器&#xff0c;存放随机变量&#xff0c;掉电数据丢失ROM&#xff1a;只读存储器&#xff0c;存放单片机的程序、指令&#xff0c;掉电数据不丢失注&#xff1a;1、冯诺依曼架构中将数据与指令存放在同一存储器中2、哈佛架构是将数据与指令存…

HTML应用指南:利用GET请求获取全国OPPO官方授权体验店门店位置信息

本篇文章将利用GET请求从OPPO官方网站或公开接口中获取官方授权体验店的分布信息&#xff0c;并通过Python编程语言中的requests库来实现HTTP请求&#xff0c;从而提取详细的门店位置数据。随着OPPO品牌的发展和市场布局的扩展&#xff0c;其官方授权体验店已经遍布全国各大城市…

Self-RAG:基于自我反思的检索增强生成框架技术解析

本文由「大千AI助手」原创发布&#xff0c;专注用真话讲AI&#xff0c;回归技术本质。拒绝神话或妖魔化。搜索「大千AI助手」关注我&#xff0c;一起撕掉过度包装&#xff0c;学习真实的AI技术&#xff01; 一、核心定义与原始论文 Self-RAG&#xff08;Self-Reflective Retri…

【YOLOv8改进 - C2f融合】C2f融合DBlock(Decoder Block):解码器块,去模糊和提升图像清晰度

YOLOv8目标检测创新改进与实战案例专栏 专栏目录: YOLOv8有效改进系列及项目实战目录 包含卷积,主干 注意力,检测头等创新机制 以及 各种目标检测分割项目实战案例 专栏链接: YOLOv8基础解析+创新改进+实战案例 文章目录 YOLOv8目标检测创新改进与实战案例专栏 介绍 摘要 文…

LLamafactory是什么?

LLamaFactory是一个专注于大型语言模型&#xff08;LLM&#xff09;训练、微调和部署的开源工具平台&#xff0c;旨在简化大模型的应用开发流程。‌1.核心功能与特点‌LlamaFactory&#xff08;全称Large Language Model Factory&#xff09;作为一站式AI开发工具平台&#xff…

Element Plus编辑表格时的页面回显(scope)

1、前提&#xff1a;自定义列模版(把id作为参数&#xff0c;传递到调用的edit函数里)<template #default"scope"><el-button type"primary" size"small" click"edit(scope.row.id)"><el-icon><EditPen /><…

河南萌新联赛2025第四场-河南大学

今天又是坐牢的一次比赛&#xff0c;恭喜获得本次比赛称号&#xff1a;挂机王&#xff0c;一个签到题能卡住两个小时&#xff0c;这两个小时简直坐的我怀疑人生&#xff0c;实在是找不出哪里错了&#xff0c;后来快结束的时候才发现少了一个等于号&#xff0c;也不至于连签到题…

【Excel】通过Index函数向下拖动单元格并【重复引用/循环引用】数据源

文章目录CASE1: 列数据源&#xff0c;向下拖动&#xff0c;每个单元重复N次步骤1&#xff1a;基本的INDEX函数步骤2&#xff1a;添加行号计算步骤3&#xff1a;添加绝对引用以便拖动CASE2:列数据源&#xff0c;向下拖动&#xff0c;每个单元重复1次&#xff0c;周而复始步骤1&a…