当前位置: 首页 > news >正文

「数组」归并排序 / if语句优化|小区间插入优化(C++)

概述

在上一篇文章中,我们介绍了快速排序以及随机快速排序:

「数组」快速排序 / 随机值优化|小区间插入优化(C++)

今天,我们来介绍归并排序。

相比于快速排序是冒泡排序融合了分治思想后形成的究极promax进化版,归并排序不来自任何一种使用分治的基础排序:它本人就是纯粹的分治。

思路

冯·诺依曼想过这么一个事:两个有序数组二路合并成一个有序数组的时间复杂度是线性的:

     i  0 1 2 3 4
arr1[i] 1 5 6 8 9
arr2[i] 0 2 3 4 7ans[i] 1 2 3 4 5 6 7 8 9

很明显, 你只需要双指针i和j分别指向两个数组,k指针指向答案数组,

每次都进行ans[k++]=min(arr1[i],arr2[j]),之后i++或j++(这要看min函数取到了谁)

就能在线性时间内得到合并好的数组。

那如果能把一个无序数组在中间一分为二成两个有序的子数组然后合并就好了,但是子数组怎么能有序呢?

那如果能把这两个子数组分成四个有序子数组然后合并就好了

那如果能把这四个子数组分成八个有序子数组然后合并就好了

.....................

那如果能把这一个数分成,诶,等等,一个数本来就有序啊

那从这里再一层一层合并上去不就好了?冯·诺依曼恍然大悟。

算法过程

来把我们的思路实现一遍。

merge_sort()

我们给出函数merge_sort,它接收一个范围,它只干两件事: 

①申请一个辅助数组。

②启动这个层层划分的过程。

让我们谈谈辅助数组:

在二路归并时,我们需要一个地方承载我们的结果。那不如就让原数组承载我们的结果,辅助数组[l,r)位置上拷贝一份原数组[l,r)位置上的值,两个指针i和j都指向辅助数组的对应位置,然后向原数组填入对应的结果。这样我们的排序结果就还放在原来的内存地址上。

void merge_sort(int arr[], int l, int r) {int* assist= new int[r-l];recursion(arr, l, r, assist);delete[] assist;
}

recursion()

我们给出递归函数recursion,它只干两件事: 

①一刀劈开接收到的数组,然后传给两个子recursion。

②在劈开的两个数组有序后启动二路合并。

*注意*:根据代码语言传统,l和r往往是一个左闭右开区间,即[l,r),表示范围取到l,但取不到r。两个子数组的范围分别是[l,m),[m,r)。

void recursion(int arr[], int l, int r, int assist[]) {if (r - l <= 1)return;int m = (l + r) / 2;recursion(arr, l, m, assist);recursion(arr, m, r, assist);merge(arr, l, m, r, assist);
}

merge() 

我们给出递归函数recursion,它只干两件事: 

①把原数组对应位置的数据拷贝给辅助数组的对应位置。

②进行一个极其简单的二路合并。

*注意*:i或j触底后将另一个数组的剩余部分通通接入合并后数组的末尾。

void merge(int arr[], int l, int m, int r, int assist[]) {memcpy(&assist[l], &arr[l], sizeof(int) * (r - l));int i, j, k;for (i = l, j = m, k = l;; k++) {if (i == m || j == r)break;if (assist[i] <= assist[j])arr[k] = assist[i++];else arr[k] = assist[j++];}while (i != m)arr[k++] = assist[i++];while (j != r)arr[k++] = assist[j++];
}

Code

void merge(int arr[], int l, int m, int r, int assist[]) {memcpy(&assist[l], &arr[l], sizeof(int) * (r - l));int i, j, k;for (i = l, j = m, k = l;; k++) {if (i == m || j == r)break;if (assist[i] <= assist[j])arr[k] = assist[i++];else arr[k] = assist[j++];}while (i != m)arr[k++] = assist[i++];while (j != r)arr[k++] = assist[j++];
}
void recursion(int arr[], int l, int r, int assist[]) {if (r - l <= 1)return;int m = (l + r) / 2;recursion(arr, l, m, assist);recursion(arr, m, r, assist);merge(arr, l, m, r, assist);
}
void merge_sort(int arr[], int l, int r) {int* assist= new int[r-l];recursion(arr, l, r, assist);delete[] assist;
}

谈谈奇数

常有人对于归并排序对于奇数长度数组的可行性产生质疑。我们来思考一下这个问题。

先想想二路归并:

     i  0 1 2 3 4
arr1[i] 1 5 6 8
arr2[i] 0 2 3 4 7ans[i] 1 2 3 4 5 6 7 8

结合上面的merge()实现代码,你能发现:两个长度不同的子数组丝毫不影响二路归并的进行。

那么奇数数组导致的中间位置不均匀丝毫不会影响整个排序。

优化方案

1.if语句优化

在一些时候,二路合并根本没必要发生:第一个有序子数组的最后一个数比第二个有序子数组的第一个数小,那么他们天然就接在了一起。

void merge_if(int arr[], int l, int m, int r, int assist[]) {if (arr[m - 1] <= arr[m])return;memcpy(&assist[l], &arr[l], sizeof(int) * (r - l));int i, j, k;for (i = l, j = m, k = l;; k++) {if (i == m || j == r)break;if (assist[i] <= assist[j])arr[k] = assist[i++];else arr[k] = assist[j++];}while (i != m)arr[k++] = assist[i++];while (j != r)arr[k++] = assist[j++];
}

2.小区间插入优化

最底层会产生大量recursion递归来对小区间二分,而他们分区的范围却极小,这是不必要的,而且相当被动。

插入排序在小区间的表现要好于快速分区,当分区小到某个程度时,我们直接转发给插入排序。

void recursion_with_insertion(int arr[], int l, int r, int assist[]) {if (r - l <= 20){insertion_sort(&arr[l], r - l); return;}int m = (l + r) / 2;recursion_with_insertion(arr, l, m, assist);recursion_with_insertion(arr, m, r, assist);merge_if(arr, l, m, r, assist);
}

Code

void merge_if(int arr[], int l, int m, int r, int assist[]) {if (arr[m - 1] <= arr[m])return;memcpy(&assist[l], &arr[l], sizeof(int) * (r - l));int i, j, k;for (i = l, j = m, k = l;; k++) {if (i == m || j == r)break;if (assist[i] <= assist[j])arr[k] = assist[i++];else arr[k] = assist[j++];}while (i != m)arr[k++] = assist[i++];while (j != r)arr[k++] = assist[j++];
}
void recursion_with_insertion(int arr[], int l, int r, int assist[]) {if (r - l <= 20){insertion_sort(&arr[l], r - l); return;}int m = (l + r) / 2;recursion_with_insertion(arr, l, m, assist);recursion_with_insertion(arr, m, r, assist);merge_if(arr, l, m, r, assist);
}
void MGsort(int arr[], int l, int r) {int* assist = new int[r - l];recursion(arr, l, r, assist);delete[] assist;
}

复杂度

时间复杂度:O(nlogn)

空间复杂度:O(n)

复杂度分析

时间分析:

把一个长度为n的数组一直二分成长度为1的数组的次数是logn次,也就是有logn层

--------------------------------------
----------------- --------------------
--------- ------- ------ -------------
--- ----- --- --  -- --- --- ---------
- - - --- - - --  -- - - - - ---- ----
- - - - - - - - - - - - - - - - - - --
——————————————————n———————————————————
—m——m——m——m——m——m——m——m——m——m——m——m——m

每一层都要对子数组两两二路合并,复杂度为O(m),m为子数组的长度 ,而每一层的全体m求和为n,故每一层的复杂度都为O(n),有logn层,故得到O(nlogn)


空间分析:

使用了长度为n的辅助数组,故为O(n)

*注意*:同一层的每次合并函数和递归函数结束后空间都会被释放,下次函数都会再次占据这块空间,空间复用使得每一层的复杂度都是O(1),函数的空间占用整体为O(logn) 。O(n+logn)=O(n)。

总结

分而治之,是为分治。

如果你的朋友问什么是分治,那么就给他讲讲归并排序。作为应用分治思想的算法,它真的纯粹到毫无保留。

 

 

相关文章:

「数组」归并排序 / if语句优化|小区间插入优化(C++)

概述 在上一篇文章中&#xff0c;我们介绍了快速排序以及随机快速排序&#xff1a; 「数组」快速排序 / 随机值优化|小区间插入优化&#xff08;C&#xff09; 今天&#xff0c;我们来介绍归并排序。 相比于快速排序是冒泡排序融合了分治思想后形成的究极promax进化版&…...

颠覆传统 北大新型MoM架构挑战Transformer模型,显著提升计算效率

挑战传统的Transformer模型设计 在深度学习和自然语言处理领域&#xff0c;Transformer模型已经成为一种标准的架构&#xff0c;广泛应用于各种任务中。传统的Transformer模型依赖于一个固定的、按深度排序的层次结构&#xff0c;每一层的输出都作为下一层的输入。这种设计虽然…...

接口优化笔记

索引 添加索引 where条件的关键自动或者order by后面的排序字段可以添加索引加速查询 索引只能通过删除新增进行修改&#xff0c;无法直接修改。 # 查看表的索引 show index from table_name; show create table table_name; # 添加索引 alter table table_name add index …...

pandas 科学计数法显示

我注意到pandas中有一个问题&#xff0c; 默认情况下&#xff0c;就是其中的数据的小数位不能超过6位&#xff0c;比如0.0000007就会被显示为0&#xff0c;这个结果如下 全部以科学技术显示 import pandas as pd import numpy as np# 设置显示格式为科学计数法 pd.options.d…...

PHP正则替换字符串中的图片地址

在PHP中&#xff0c;可以使用preg_replace()函数来实现正则表达式的替换功能。以下是一个简单的例子&#xff0c;演示如何替换字符串中的图片地址。 double $str 图片地址1&#xff1a;<img src"http://example.com/image1.jpg"> 图片地址2&#xff1a;<i…...

基于多商户AI智能名片商城小程序的粉丝忠诚度提升策略:深度融合足额法则与多维度激励体系

摘要&#xff1a;在数字化浪潮的推动下&#xff0c;多商户AI智能名片商城小程序以其独特的商业模式和技术优势&#xff0c;正逐步成为连接商家与消费者&#xff0c;特别是粉丝群体的重要平台。本文深入探讨了如何通过深度融合足额法则与多维度激励体系&#xff0c;有效提升多商…...

BigDecimal高精度运算

1. BigDecimal是什么类型&#xff0c;为什么可以转为double BigDecimal 是 Java 中用于表示任意精度的十进制数的类。它主要用于金融和商业计算&#xff0c;能够提供比 double 类型更高精度的运算&#xff0c;特别是在处理货币等需要精确计算的场景中。 1.1 BigDecimal 的基…...

C/C++实现蓝屏2.0

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 前…...

Unity音频管理器插件AudioToolKit

Unity音频管理器插件AudioToolKit 介绍AudioToolKit介绍具体用法总结 介绍 最近在自己写音频管理器的时候在网上发现了一款比较好用并且功能很全的一个音频管理插件&#xff0c;叫做AudioToolKit的插件。 如果需要的可以直接从我资源中找AudioToolKit。 AudioToolKit介绍 A…...

搜维尔科技:驾驶模拟器背后的技术: Varjo的虚拟/混合现实 (VR/XR)提供独特的优势,最终加快汽车开发创新的步伐

专业驾驶模拟器广泛应用于车辆开发&#xff0c;帮助汽车行业在开发过程的早期做出更好的设计决策。总体目标是为测试驾驶员提供最真实的驾驶体验&#xff0c;包括动态动作和声音&#xff0c;并测试控制算法或辅助系统等功能。环境越真实&#xff0c;驾驶员的体验就越接近最终车…...

OSL 冠名赞助Web3峰会 “FORESIGHT2024”圆满收官

OSL 望为香港数字资产市场发展建设添砖加瓦 &#xff08;香港&#xff0c;2024 年 8 月 13 日&#xff09;- 8 月 11 日至 12 日&#xff0c; 由 香港唯一专注数字资产的上市公司 OSL 集团&#xff08;863.HK&#xff09;冠名赞助&#xff0c;Foresight News、 Foresight Ventu…...

LeetCode 3148.矩阵中的最大得分:每个元素与其左或上元素之差的最大值(原地修改O(1)空间)

【LetMeFly】3148.矩阵中的最大得分&#xff1a;每个元素与其左或上元素之差的最大值&#xff08;原地修改O(1)空间&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/maximum-difference-score-in-a-grid/ 给你一个由 正整数 组成、大小为 m x n 的矩阵 g…...

主流的开源大型语言模型

本期我们来聊聊目前主流的开源大型语言模型。这些模型就像是AI界的超级英雄&#xff0c;各具特色&#xff0c;为我们的研究和开发提供了强大的力量。&#x1f680; GPT-Neo&#xff1a;这是EleutherAI的杰作&#xff0c;它模仿了OpenAI的GPT-3。GPT-Neo虽然规模小一些&#xf…...

【自动驾驶】话题通信

目录 构建发布者构建订阅者编写lanch文件自动启动节点测试运行ROS的目录结构 切换到工作空间的src目录下&#xff1a; 构建发布者 catkin_create_pkg publisher std_msgs rospy roscpp编写发布者程序&#xff1a; // 1.包含头文件 #include "ros/ros.h" #include &…...

【Linux】中的软件安装:深入探索RPM、SRPM与YUM

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Linux的起源与发展 2、RPM、SRPM与YUM的简要介…...

uniapp自定义请求头信息header

添加请求头&#xff1a;uniapp自定义请求头信息header&#xff0c;如下&#xff1a;添加tenant-id参数 代码...

SpringBoot整合Liquibase

1、是什么&#xff1f; Liquibase官网 Liquibase是一个开源的数据库管理工具&#xff0c;可以帮助开发人员管理和跟踪数据库变更。它可以与各种关系型数据库和NoSQL数据库一起使用&#xff0c;并提供多种数据库任务自动化功能&#xff0c;例如数据库迁移、版本控制和监控。Li…...

虚幻5|给武器添加碰撞检测与伤害

本章内容衔接上两章&#xff0c;需要完成上两章才能用本章内容 虚幻5|角色武器装备的数据库学习&#xff08;不只是用来装备武器&#xff0c;甚至是角色切换也很可能用到&#xff09;-CSDN博客虚幻5|普通攻击&#xff0c;使用接口更方便-CSDN博客 如有疑问&#xff0c;可访问…...

RESTful API设计指南:构建高效、可扩展的Web服务

目录 引言 一.RESTful API概述 二.设计原则 2.1. 资源导向 2.2. 使用标准的HTTP方法 2.3. 无状态通信 2.4. 可缓存响应 2.5. 分层系统 2.6. 按需加载代码&#xff08;可选&#xff09; 2.7. HATEOAS 三.最佳实践 3.1. 明确资源和子资源 3.2. 使用合适的HTTP状态码 …...

黑马头条vue2.0项目实战(九)——编辑用户资料

目录 1. 创建组件并配置路由 2. 页面布局 3. 展示用户信息 4. 修改昵称 5. 修改性别 6. 修改生日 7. 修改头像 7.1 图片上传预览 7.2 使用纯客户端的方式处理用户头像上传预览 7.3 头像裁切 7.4 纯客户端的图片裁切上传流程 7.5 Cropper.js 图片裁剪器的基本使用 …...

从豆瓣到StyleTalk:手把手教你用真实场景数据微调你的中文对话模型

从豆瓣到StyleTalk&#xff1a;手把手教你用真实场景数据微调你的中文对话模型 当你已经掌握了基座模型微调的基础技能&#xff0c;如何让模型真正理解特定领域的专业术语&#xff0c;或是模仿某种独特的说话风格&#xff1f;本文将带你深入实战&#xff0c;从数据清洗到效果评…...

Python大麦网智能抢票脚本:三分钟搭建你的自动购票系统

Python大麦网智能抢票脚本&#xff1a;三分钟搭建你的自动购票系统 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 还在为抢不到心仪的演唱会门票而烦恼吗&#xff1f;每次开…...

从vector的push_back看C++的‘完美转发’:一个emplace_back如何省掉一次临时对象构造

从vector的emplace_back揭秘C完美转发的魔法 在C的世界里&#xff0c;vector作为最常用的容器之一&#xff0c;其性能优化一直是开发者关注的焦点。当我们向vector添加元素时&#xff0c;push_back和emplace_back这两个看似相似的函数&#xff0c;背后却隐藏着现代C最精妙的语言…...

“吸收液中间冷却与调整填料高度组合应用” — aspenplusv11百万吨碳捕集系统的关键优化策略

aspenplusv11百万吨碳捕集系统&#xff0c;复配胺溶液&#xff0c;工艺流程优化&#xff0c;吸收液中间冷却、调整吸收段填料高度、贫液入塔分流等。 吸收液中间冷却与调整填料高度组合应用凌晨三点的实验室&#xff0c;咖啡杯底结着褐色的垢。盯着Aspen Plus界面里那个持续报警…...

我的上课记

...

深入解析 iOS 上 fixed 底栏与滚动容器的手势冲突:从 H5 修复到原生根治

在移动端 H5 开发中,我们时常遇到这样的场景:页面底部有一个固定定位(position: fixed)的按钮栏或底栏,上方是一个可滚动的长列表。在 iOS 设备上,当用户尝试从底部 fixed 区域起手向上滑动时,列表却纹丝不动,仿佛被“粘”住了。这个现象不是偶发 bug,而是 iOS 对 fix…...

【2026年最新600套毕设项目分享】基于springboot+vue的无人机共享管理系统(14299)

有需要的同学&#xff0c;源代码和配套文档领取&#xff0c;加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码&#xff08;前后端源代码SQL脚本&#xff09;配套文档&#xff08;LWPPT开题报告/任务书&#xff09;远程调试控屏包运行一键启动项目&…...

无需安装jupyter notebook,在快马平台5分钟搭建你的第一个数据分析原型

今天想和大家分享一个快速搭建数据分析原型的经验。作为一个经常需要验证想法的数据分析师&#xff0c;最头疼的就是每次换电脑或重装系统后配置Jupyter Notebook环境的过程。最近发现了一个超省心的解决方案&#xff0c;不用本地安装就能直接开搞数据分析。 为什么选择云端Ju…...

超越rviz_satellite:用Mapviz实现高精度SLAM地图与卫星图叠加(附开源数据集测试)

超越rviz_satellite&#xff1a;用Mapviz实现高精度SLAM地图与卫星图叠加&#xff08;附开源数据集测试&#xff09; 当自动驾驶车辆在复杂城市环境中穿行&#xff0c;或是无人机在未知区域执行勘探任务时&#xff0c;将实时构建的SLAM地图与卫星影像精准叠加&#xff0c;已成…...

手把手教你搞定RK3568 Android11平台上的AIC8800 WiFi6模块驱动(附常见报错解决)

RK3568 Android11平台AIC8800 WiFi6模块驱动移植全流程指南 在嵌入式开发领域&#xff0c;WiFi模块的集成往往是项目推进的关键环节。AIC8800作为一款支持WiFi6的芯片&#xff0c;凭借其优异的性能和功耗表现&#xff0c;正逐渐成为RK3568等主流嵌入式平台的热门选择。本文将系…...