数据结构:归并排序

归并排序

时间复杂度O(N*logN)

如果两个序列有序,通过归并,可以让两个序列合并后也有序,变成一个有序的新数组

对于一个数组,如果他的左右区间都有序,就可以进行归并了

归并的方法

将数组的左右两个有序区间比较,每次都取出一个最小的,然后放入临时数组(不能在原数组上修改,因为修改原数组需要挪动数据,时间复杂度高)

但是对于任意一个数组而言,他的左右区间并不有序,如果想要使用归并排序,就要想办法取出数组的有序区间

这里可以使用递归拆分数组,当数组足够小(每一个区间都只有一个数据时),该区间就一定有序,就可以使用归并排序了

可以设置一个temp数组,用来存储分解的区间并进行排序,归并排序完成后再将temp拷贝回原数组的对应区间,直到递归完全结束(原数组有序)

代码

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin == end)return;int mid = (begin + end) / 2;//开始递归_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);//归并int begin1 = begin;int end1 = mid;int begin2 = mid + 1;int end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//判断哪个区间全部放入tmp,再将另一个区间全部放入tmpwhile (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}//将归并好的区间拷贝会原数组memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!");return;}_MergeSort(a, 0, n - 1, tmp);//释放tmpfree(tmp);tmp = NULL;
}

归并排序的非递归方法

归并排序需要将递归改为循环

不方便使用栈来实现,因为不同于快速排序类似前序遍历,归并排序的递归类似于二叉树的后序遍历

问题核心

归并排序需要两个区间进行归并,

而如果使用栈,则当取到需要归并的两个区间的第二个

第一个区间就已经被pop出栈了,无法知道哪个区间需要和当前区间归并

解决方法

可以将数组看作一个一个数(一个数就是一个区间),然后每两个就进行归并

将归并后的两个数再看作一个区间,再与其他区间进行归并

即直接从叶子节点向前走(回溯)

可以设置gap = 2^n来归并每组数据

设置每个区间的首尾为b n(begin n),e n(end n)

eg.

由图可知,e1 = b1 + gap - 1,b2 = b1 + gap

后续同理

基础代码框架(存在问题)

根据当前的思路,可以写出大概的代码

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n)//一直归并到原数组大小{for (int j = 0; j < n; j += 2 * gap)//保证每一个区间都被取到{int begin1 = j, end1 = j + gap - 1;int begin2 = j + gap, end2 = begin2 + gap - 1;int i = j;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//判断哪个区间全部放入tmp,再将另一个区间全部放入tmpwhile (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + j, tmp + j, sizeof(int) * 2 * gap);}gap *= 2;}free(tmp);tmp == NULL;
}

代码问题(数组越界)

当前代码可能存在数组的越界访问

理论上来讲,除了begin1 = j < n外,其他的begin2, end1, end2都可能越界

因此需要分开处理这些问题

1.end1越界

end1越界(end1后面就没有数据了)说明当前没有第二组数据,而第一组只有一部分,且有序

因此当前这一组就不需要再进行归并了,直接让这一组准备下一层归并

2.begin2越界

begin2越界也不用归并,原因与end1越界相同

3.end2越界

end2越界需要归并,因为在begin2与end2中还存在部分有序数据,因此需要归并组成新区间

但是想要归并就需要修正end2,保证end2不再越界

4.修改后的代码

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n)//一直归并到原数组大小{for (int j = 0; j < n; j += 2 * gap)//保证每一个区间都被取到{int begin1 = j, end1 = j + gap - 1;int begin2 = j + gap, end2 = begin2 + gap - 1;int i = j;if (end1 >= n || begin2 >= n)//end1和begin2越界,跳出当前循环,不归并{break;}if (end2 >= n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//判断哪个区间全部放入tmp,再将另一个区间全部放入tmpwhile (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));//使用修正后的区间长度}gap *= 2;}free(tmp);tmp == NULL;
}

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

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

相关文章

《自动机理论、语言和计算导论》阅读笔记:p68-p114

《自动机理论、语言和计算导论》学习第4天&#xff0c;p68-p114总结&#xff0c;总计47页。 一、技术总结 1.inverted indexes 明白单词的意思是“反转的索引”&#xff0c;但是不明白其在书中具体指什么&#xff0c;去查询资料的话需要花很不多时间&#xff0c;先继续往下看…

使用Leaflet.rotatedMaker进行航班飞行航向模拟的实践

目录 前言 一、Leaflet的不足 1、方向插件 2、方向控制脚本说明 二、实时航向可视化实现 1、创建主体框架 2、飞机展示 3、位置和方位模拟 三、成果及分析 1、成果展示 2、方向绑定解读 总结 前言 众所周知&#xff0c;物体在空间中的运动&#xff08;比如飞行、跑步…

SSM框架学习——MyBatis关联映射

MyBatis关联映射 为什么要关联映射 实际开发中&#xff0c;对数据库操作常常会涉及多张表&#xff0c;所以在OOP中就涉及对象与对象的关联关系。针对多表操作&#xff0c;MyBatis提供关联映射。 关联关系概述 一对一&#xff1a;A类中定义B类的属性b&#xff0c;B类中定义A…

spring-boot之shiro安全框架配置使用

shiro架构&#xff08;外部&#xff09; shiro架构(内部) 具体API操作 获取当前的用户对象 Subject currentUser SecurityUtils.getSubject();通过当前用户拿到session Session session currentUser.getSession(); session.setAttribute("someKey", "aValu…

如何在Linux系统运行RStudio Server并实现无公网IP远程访问【内网穿透】

文章目录 推荐 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

惊喜!这一国产数据库认证考试限免了!

今年第一个季度过去了&#xff0c;又到春暖花开时&#xff0c;群里的小伙伴开始躁动不安&#xff0c;焦虑加倍。 有考虑被 cloud 淘汰的&#xff0c;有考虑被共享 emp 的&#xff0c;还有问粗粮 car 能不能当专车开的。 但技术人&#xff0c;更多时间还是在讨论正能量&#xff…

c++的STL(6)-- map和multimap

map和multimap概述 map和multimap中存储的是使用pair对象的键值对。 map和multimap底层也是使用红黑树的数据结构进行实现的。所以&#xff0c;map和multimap内部存储的键值对也是有序的。并且内部数据也是使用链表的形式进行关联。所以其的迭代器和指针&#xff0c;也只能进行…

基于ssm端游账号销售管理系统论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对端游账号销售信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…

认识docker

认识docker 1.镜像和容器2.MySQL镜像命令解读3.docker常用命令4.数据挂载&#xff08;1&#xff09;数据卷&#xff08;2&#xff09;本地目录挂载 5.自定义镜像&#xff08;1&#xff09;Dockerfile 6.容器网络互连7、参考 1.镜像和容器 当我们利用 Docker 安装应用时&#x…

【BlossomRPC】接入注册中心

文章目录 NacosZookeeper自研配置中心 RPC项目 配置中心项目 网关项目 这是BlossomRPC项目的最后一篇文章了&#xff0c;接入完毕注册中心&#xff0c;一个完整的RPC框架就设计完成了。 对于项目对注册中心的整合&#xff0c;其实我们只需要再服务启动的时候将ip/port/servic…

区块链食品溯源案例实现(二)

引言 随着前端界面的完成&#xff0c;我们接下来需要编写后端代码来与区块链网络进行交互。后端将负责处理前端发送的请求&#xff0c;调用智能合约的方法获取食品溯源信息&#xff0c;并将结果返回给前端。 通过前后端的整合&#xff0c;我们可以构建一个食品溯源系统&#xf…

spring boot3登录开发-3(2短信验证登录/注册逻辑实现)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 上文衔接 内容简介 功能分析 短信验证登录实现 1.创建交互对象 用户短信登录/注册DTO 创建用户登录VO…

Linux(CentOS)安装Redis教程_简单快捷

一、安装依赖 因为redis是用C语言开发的&#xff0c;所以在安装之前需要确定是否安装gcc环境&#xff08;gcc -v&#xff09;&#xff0c;如果没有安转可以执行一下命令进行安装 [rootlocalhost ~]# yum install -y gcc 二、下载安装包 1.在官网先进行下载 官网地址&#x…

【接口测试】Postman(一)--接口测试知识准备

1.0 前言 ​ 应用程序编程接口&#xff08;Application Programming Interface, API&#xff09;是这些年来最流行的技术之一&#xff0c;强大的Web应用程序和领先的移动应用程序都离不开后端强大的API。API技术的应用给系统开发带来了便利&#xff0c;但也对测试人员提出了更…

Ubuntu joystick 测试手柄 xbox

Ubuntu joystick 测试手柄 xbox 测试使用Ubuntu20.04 测试环境在工控机 安装测试 实际测试使用的手柄是北通阿修罗2pro 兼容xbox Ubuntu20.04主机 连接手柄或者无线接收器后查看是否已经检测到&#xff1a; ls /dev/input找到输入中的 js0 即为手柄输入 需要安装joysti…

浅谈 kafka

引言 同事在公司内部分享了关于 kafka 技术一些相关的内容&#xff0c;所以有了这篇文章&#xff1b;部分图片选自网络摘抄&#xff1b; 1 Kafka概述 1.1 定义 Kafka传统定义&#xff1a;kafka是一个分布式的基于发布/订阅模式的消息队列。 Kafka最新定义&#xff1a;kafka…

【讲解下Gitea】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

突破编程_前端_JS编程实例(分割窗体组件)

1 开发目标 分隔窗体组件旨在提供灵活的窗体分隔功能&#xff0c;支持横向分割与纵向分隔两种类型&#xff0c;并具备拖拽调整窗体比例的功能&#xff0c;同时提供最小比例设置&#xff0c;以防止窗体被过度缩小&#xff1a; 2 详细需求 2.1 分隔窗体类型 &#xff08;1&…

AI改写文案的注意事项

AI改写文案的注意事项 随着人工智能技术的不断发展&#xff0c;AI改写文案成为了一种新兴的应用场景。通过AI改写文案&#xff0c;可以快速生成大量内容&#xff0c;节省时间和人力成本&#xff0c;但在实际应用中也需要注意一些问题和注意事项。 1. 确保内容原创性 尽管AI改…

个人做量化交易是否可行呢?

考虑个人做量化交易&#xff0c;需要完成两步&#xff1a; 解决3个“什么”的问题&#xff1a;“你要在什么时间&#xff1f;交易什么标的&#xff1f;交易数量是多少&#xff1f;”把你的想法准确地表达出来&#xff0c;告诉交易下单系统 也就是自己形成策略----自己去实现。…