《算法题讲解指南:优选算法-分治-归并》--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

Flutter 三方库 opml 的鸿蒙化适配指南 - 支持大容量订阅源解析、符合 OPML 2.0 规范与 RSS 管理器核心适配

Flutter 三方库 opml 的鸿蒙化适配指南 - 支持大容量订阅源解析、符合 OPML 2.0 规范与 RSS 管理器核心适配

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 opml 的鸿蒙化适配指南 - 支持大容量订阅源解析、符合 OPML 2.0 规范与 RSS 管理器核心适配 前言 在进行 Flutter for OpenHarmony 的阅读类、播客类或 RSS 订阅类应用开发时,支持标准的 OPML(Outline Processor Markup Language)导入与导出是必选功能。opml 库是一个专门用于解析和生成 OPML 文件的 Dart 库。本文将探讨如何在鸿蒙系统下,利用该库高效管理用户的订阅树结构。 一、原理解析 / 概念介绍 1.1 基础原理 OPML 本质上是一种基于

By Ne0inhk
【高级终端Termux】在安卓手机/平板上使用Termux 搭建 Debian 环境并运行 PC 级 Linux 应用教程(含安装WPS,VS Code)

【高级终端Termux】在安卓手机/平板上使用Termux 搭建 Debian 环境并运行 PC 级 Linux 应用教程(含安装WPS,VS Code)

Termux 搭建 Debian 环境并运行 PC 级 Linux 应用教程 一、前言 1. 背景 众所周知,最新搭载澎湃OS和鸿蒙OS的平板都内置了PC级WPS,办公效率直接拉满(板子终于从“泡面盖”升级为“生产力”了)。但问题来了:如果不是这两个系统,难道我们只能继续用平板盖泡面吗?当然不是!折腾了很长时间后,总算找到了一个好玩的东西:高级终端Termux 。现在,不仅能随时随地用WPS改文档,还能VSCode优雅地敲代码,再也不用背着电脑乱跑了。 由于每次搭建环境时都要去不同的平台找不同功能,有时还找不到,所以我决定自己写一篇博客,方便自己以后再搭建时直接“Ctrl C + Ctrl V”,顺便分享给有同样需求的小伙伴们。话不多说,直接开整! 2. 准备工作 * 一部安卓手机:性能越好,折腾起来越顺畅。 * Termux 应用: 不想去F-droid下载的看过来

By Ne0inhk
Flutter 三方库 filterator 的鸿蒙化适配指南 - 掌握声明式数据流过滤技术、助力鸿蒙应用构建极速且易维护的复杂列表筛选逻辑

Flutter 三方库 filterator 的鸿蒙化适配指南 - 掌握声明式数据流过滤技术、助力鸿蒙应用构建极速且易维护的复杂列表筛选逻辑

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 filterator 的鸿蒙化适配指南 - 掌握声明式数据流过滤技术、助力鸿蒙应用构建极速且易维护的复杂列表筛选逻辑 前言 在 OpenHarmony 鸿蒙应用全场景信息交互的开发中,“数据清洗与过滤(Data Filtering)”是提升用户体验的关键环。当你需要在一个包含上万件商品的电商列表中,同时根据“价格区间”、“用户评分”、“物流时效”以及“是否有货”进行复合筛选时,嵌套的 if-else 或繁琐的迭代逻辑会让代码迅速变得臃肿且难以调试。filterator 作为一个专为 Dart 集合设计的声明式过滤利器,旨在通过链式调用与逻辑组合,将复杂的数据筛选过程转化为语义清晰、模块化的流式配置。本文将介绍如何在鸿蒙端利用 filterator 打造极致的数据交互体验。 一、原原理分析 / 概念介绍 1.1 基础原理 filterator 的核心逻辑是 基于谓词逻辑的集合管道过滤器

By Ne0inhk
Flutter 三方库 vendure 的适配鸿蒙实战 - 驾驭核心电商交易总网,实现 OpenHarmony 下的大并发 GraphQL 无头电商网关与数据强防腐

Flutter 三方库 vendure 的适配鸿蒙实战 - 驾驭核心电商交易总网,实现 OpenHarmony 下的大并发 GraphQL 无头电商网关与数据强防腐

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 vendure 的适配鸿蒙实战 - 驾驭核心电商交易总网,实现 OpenHarmony 下的大并发 GraphQL 无头电商网关与数据强防腐 前言 随着鸿蒙(OpenHarmony)生态的全球化出海,超级应用与万物互联的电商新纪年已经拉开帷幕。我们在将手机、平板、车载大屏甚至穿戴设备接入商城入口时,必须面对传统 RESTful 接口带来的巨大挑战:接口散乱、冗余数据多、联调效率低。 在处理类似 0308 批次这种千万级大字段的商品详情系统时,如果前端对后端接口的变动缺乏抗崩御能力,一次小小的结构调整就可能导致全链条的业务断裂,直接造成现金流的损失。我们需要一种“逻辑高层编排、数据按需即取、边界强悍防御”的接口总网。vendure 库正是为此而生的 GraphQL 客户端架构重炮。本文将详细揭秘它如何帮助你在鸿蒙端打造一套坚不可摧的交易底盘。 一、原理解析 / 概念介绍 1.

By Ne0inhk