《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对

《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》

《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--优选算法

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

47.归并排序

题目链接:

题目描述:

题目示例:

解法(归并排序):

算法思路:

C++算法代码:

算法总结及流程解析:

48.数组中的逆序对

题目链接:

题目描述:

题目示例:

解法(利用归并排序的过程——分治):

算法思路:

C++算法代码:

算法总结及流程解析:

结束语


47.归并排序

题目链接:

215. 数组912. 排序数组 - 力扣(LeetCode)215. 数组

题目描述:

题目示例:

解法(归并排序):

算法思路:

      归并排序的流程充分的体现了「分而治之」的思想,大体过程分为两步:

      分:将数组一分为二为两部分,一直分解到数组的长度为1,使整个数组的排序过程被分为「左半部分排序」+「右半部分排序」;
      治:将两个较短的「有序数组合并成一个长的有序数组」,一直合并到最初的长度。

C++算法代码:

class Solution { public: //归并排序的算法 vector<int> tmp; //用于存放两个有序数组合并后的结果 void mergesort(vector<int>& nums, int left, int right) { if(left == right) { return; } //1、选择中间点划分区间 int mid = (right - left) / 2 + left; //将数组分成两块:[left, mid] [mid + 1, right] //2、把左右区间排序 mergesort(nums, left, mid); mergesort(nums, mid + 1, right); //3、将两个数组合并成一个有序数组 int cur1 = left, cur2 = mid + 1, i = 0; while(cur1 <= mid && cur2 <= right) { tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++]; } //还有一边数组没有合并完 while(cur1 <= mid) { tmp[i++] = nums[cur1++]; } while(cur2 <= right) { tmp[i++] = nums[cur2++]; }//只会进其中一个循环 //将两个数组有序合并到tmp中后,再还原给原数组nums对应部分位置 for(int i = left; i <= right; i++) { nums[i] = tmp[i - left]; //tmp数组每次都是以开头下标0的位置合并两个数组 } } vector<int> sortArray(vector<int>& nums) { //归并实现: tmp.resize(nums.size()); mergesort(nums, 0, nums.size() - 1); return nums; } };

算法总结及流程解析:

48.数组中的逆序对

题目链接:

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

题目描述:

题目示例:

解法(利用归并排序的过程——分治):

算法思路:

      ⽤归并排序求逆序数是很经典的⽅法,主要就是在归并排序的合并过程中统计出逆序对的数量,也就是在合并两个有序序列的过程中,能够快速求出逆序对的数量。

      我们将这个问题分解成⼏个⼩问题,逐⼀破解这道题。
      (注意:默认都是升序,如果掌握升序的话,降序的归并过程也是可以解决问题的。)

      先解决第⼀个问题,为什么可以利⽤归并排序?

      如果我们将数组从中间划分成两个部分,那么我们可以将逆序对产⽣的⽅式划分成三组:

      逆序对中两个元素:全部从左数组中选择

      逆序对中两个元素:全部从右数组中选择

      逆序对中两个元素:⼀个选左数组另⼀个选右数组

      根据排列组合的分类相加原理,三种种情况下产⽣的逆序对的总和,正好等于总的逆序对数量。

      ⽽这个思路正好匹配归并排序的过程:

      先排序左数组;

      再排序右数组;

      左数组和右数组合⼆为⼀。

      因此,我们可以利⽤归并排序的过程,先求出左半数组中逆序对的数量,再求出右半数组中逆序对的数量,最后求出⼀个选择左边,另⼀个选择右边情况下逆序对的数量,三者相加即可。

      解决第⼆个问题,为什么要这么做?

      在归并排序合并的过程中,我们得到的是两个有序的数组。我们是可以利⽤数组的有序性,快速统计出逆序对的数量,⽽不是将所有情况都枚举出来。• 最核⼼的问题,如何在合并两个有序数组的过程中,统计出逆序对的数量?合并两个有序序列时求逆序对的⽅法有两种:

1. 快速统计出某个数前⾯有多少个数⽐它⼤;

2. 快速统计出某个数后⾯有多少个数⽐它⼩;

C++算法代码:

class Solution { public: int count = 0; vector<int> tmp;//用于存放两个有序数组合并后的结果 int reversePairs(vector<int>& record) { tmp.resize(record.size()); mergesort(record, 0, record.size() - 1); return count; } void mergesort(vector<int>& nums, int left, int right) { if(left >= right)//正常归并排序递归的结束条件不用包含left>right //因为归并是分两块,最小的情况就是两个数分成各一个,不存在越界的情况 //但是此题有空数组的案例,所以一开始left=0,right=-1,要考虑在内 { return; } //1、选择中间点划分区间 int mid = (right - left) / 2 + left; //将数组分成两块:[left, mid] [mid + 1, right] //2、把左右区间排序 mergesort(nums, left, mid); mergesort(nums, mid + 1, right); //3、判断两个有序数组一左一右的逆序对个数,并且将两个数组合并成一个有序数组 int cur1 = left, cur2 = mid + 1, i = 0; //(1)将数组排成升序的思路: while(cur1 <= mid && cur2 <= right) { if(nums[cur1] <= nums[cur2]) { tmp[i++] = nums[cur1++]; } else { //当第一次遇见nums[cur1] > nums[cur2],说明[cur1, mid]区间所有值都大于nums[cur2] //计算当前nums[cur2]的逆序对个数 count += mid - cur1 + 1; tmp[i++] = nums[cur2++]; } } //(2)将数组排成降序的思路: // while(cur1 <= mid && cur2 <= right) // { // if(nums[cur1] > nums[cur2]) // { // count += right - cur2 + 1; // tmp[i++] = nums[cur1++]; // } // else // { // tmp[i++] = nums[cur2++]; // } // } //处理还没有排序的剩余部分 while(cur1 <= mid) { tmp[i++] = nums[cur1++]; } while(cur2 <= right) { tmp[i++] = nums[cur2++]; } //将两个数组有序合并到tmp中后,再还原给原数组nums对应部分位置 for(int i = left; i <= right; i++) { nums[i] = tmp[i - left]; //tmp数组每次都是以开头下标0的位置合并两个数组 } } };

算法总结及流程解析:

结束语

      到此,47.归并排序,48.数组中的逆序对 这两道算法题就讲解完了。 归并排序采用分治思想,先将数组不断二分至单个元素,再通过有序合并操作完成排序,时间复杂度为O(nlogn)。希望大家能有所收获!

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk