【LeetCode力扣】LCR170 使用归并排序的思想解决逆序对问题(详细图解)

目录
1、题目介绍
2、解题思路
2.1、暴力破解法
2.2、归并排序思想
2.2.1、画图详细讲解
2.2.2、归并排序解决逆序对的代码实现

1、题目介绍
首先阅读题目可以得出要点,即当前数大于后数时则当作一个【逆序对】,而题目是要求在一个数组中计算一共存在多少个这样的逆序对并输出结果。
原题链接:LCR 170. 交易逆序对的总数 - 力扣(LeetCode)
2、解题思路
2.1、暴力破解法
看到这里的第一反应就是这不是很简单吗?心想着这困难题也不过如此吧(笑)。
就是直接使用暴力破解法,只需要两个for循环嵌套,一个record[ i ]在原地,另一个record[ j ]将后面所有遍历一遍,只要比record[ i ]的小就将计算器count加1,然后i++再从头遍历,知道找完所有,最后返回计数器count即可,于是便奋笔疾书写了起来。
int reversePairs(int* record, int recordSize) {if (recordSize == 0){return 0;}int count = 0;int i = 0, j = 0;for (i = 0; i < recordSize - 1; i++){for (j = i + 1; j < recordSize; j++){if (record[i] > record[j]){count++;}}}return count;
}
当写完然后测试了简答数据无误,然后自信满满地点击提交,结果却直接被打脸。
看了报错结果才恍然大悟,看题目时漏看了数组长度。
题目设置的数组长度最长可达 50000,因此使用暴力破解法虽然非常简单,但是时间复杂度也非常大为O(n^2),这肯定会超出时间限制的。因此需要使用一种时间复杂度较小的遍历。到了这里才终于理解了这道题为什么是困难题!!!
2.2、归并排序思想
这里思考了一下,只要能找到 一种时间复杂度低,并且通过比较排序的算法,在比较排序的过程中顺便进行记录,这样的化就能够解决这个问题了。
于是归并排序便浮现在眼前,归并排序的时间复杂度很低只有O(nlogn),并且也是通过比较的方式进行排序,那么只需要在传统的归并排序算法中添加一些用于记录前者大于后者计算器即可。
简单回顾一下归并排序的知识:
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用递归或者说是分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序,它包含这样三个步骤:
- 分解:把长度为n的待排序序列分成两个长度为n/2的子序列。例如L代表头部,M代表中点,R代表尾部,将[ L,R ]分成[ L,M ]和[ M+1,R ]。
- 解决:使用归并排序递归地排序两个子序列。
- 合并:将两个排序好的子序列[ L,M ]和[ M+1,R ]合并成一个最终的排序序列。
在待排序序列长度为 1 的时候,递归开始「回升」,因为我们默认长度为 1 的序列是排好序的。简单来说就是:当分到单个子序列只剩下一个数字时,一个数字就是天然了有序,即此时左侧和右侧都排好序了,然后递归进行回升操作。
由于篇幅原因,如果想要更加详细地了解有关归并排序的知识,可以前往往期博客中阅读:
【算法与数据结构】归并排序的代码实现(详细图解)以及master公式的讲解_Hacynn的博客-CSDN博客
https://blog.csdn.net/zzzzzhxxx/article/details/133516177?spm=1001.2014.3001.5501
2.2.1、画图详细讲解
假设我们通过归并排序的「回升」操作已经得到了两个已排序的子序列并等待合并,这两个子序列假设为:左子序列[ 8,15,17,22,35 ]和右子序列[ 9,12,15,30,38 ]。然后用malloc开辟辅助数组help,并定义一个变量sum用于记录当前逆序对的个数。
一开始我们先用p1指向左子序列的头部,p2指向右子序列的头部,i指向help数组的头部。

此时开始比较p1和p2指向的值,我们发现p1小于p2,不符合逆序对的前者大于后者,因此不做其他操作,直接将值放入help数组 i 位置中,并且分别进行p1++和i++,使指向下一个元素。

重复p1和p2的比较操作,发现此时p1大于p2,此时记录逆序对的sum本应该自增1。
但是这里我们可以发现一个规律:因为左子序列是有序的,如果p1此时的数比p2大,那就证明p1后面的数字也依然比p2大,因此逆序对应该有M-p1+1即4个,分别是:(15,9)、(17,9)、(22,9)、(35,9)。
所以此时sum应该+4,并将p2所指的较小值存入help中。

i++、p2++;
此时p1仍然大于p2,再次使用M-p1+1计算出逆序对为4个,分别是(15,12)、(17,12)、(22,12)、(35,12)。
此时 sum = sum+4,并将12存入help中。

i++,p2++;
注意特殊:此时p1 == p2,该算法在等于时尤为关键,当相等时应该将左侧p1的15放入help中并p1++,如果放的是右侧p2的15存入,就会丢失一部分逆序对,这个原理读者可以自己画图理解。

剩下的部分依然按照此规律进行操作,最终算出该轮遍历的逆序对数sum。
2.2.2、归并排序解决逆序对的代码实现
int ExternalSort(int* arr, int L, int M, int R)
{int* help = (int*)malloc(sizeof(int) * (R - L + 1));int p1 = L;int p2 = M + 1;int sum = 0;int i = 0;while ((p1 <= M) && (p2 <= R)){//p1 大于 p2 则计算出逆袭对个数并加入sum中,p1小于p2时则给0(即不操作)sum += arr[p1] > arr[p2] ? (M - p1 + 1) : 0;//较小的数拷贝进help数组,相等时拷贝p1指针的数help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];}//当有一个子序列遍历完了,则将另外一个子序列中剩余的数据全部放入help数组中while (p1 <= M) { help[i++] = arr[p1++]; }while (p2 <= R) { help[i++] = arr[p2++]; }//将辅助数组help中已经合并完成的数据传回给原数组arr的相应位置for (i = 0; i < (R - L + 1); i++){arr[L + i] = help[i];}free(help);help = NULL;return sum; //返回本轮操作的sum值}int MergeSort(int* arr, int L, int R)
{if (L == R){return 0;}int mid = L + (R - L) / 2;return MergeSort(arr, L, mid) + //对拆分的左子序列进行递归操作MergeSort(arr, mid + 1, R) + //对拆分的右子序列进行递归操作ExternalSort(arr, L, mid, R); //外部排序并计算出逆序对sum
}int reversePairs(int* record, int recordSize) {int ret = 0;if (recordSize == 0) //对空数组进行判断{return 0;}ret = MergeSort(record, 0, recordSize - 1);return ret;
}
到这里,关于本题的解题过程就结束了 。

如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
相关文章:
【LeetCode力扣】LCR170 使用归并排序的思想解决逆序对问题(详细图解)
目录 1、题目介绍 2、解题思路 2.1、暴力破解法 2.2、归并排序思想 2.2.1、画图详细讲解 2.2.2、归并排序解决逆序对的代码实现 1、题目介绍 首先阅读题目可以得出要点,即当前数大于后数时则当作一个【逆序对】,而题目是要求在一个数组中计算一共存…...
python经典百题之一个素数能被几个9整除
题目:判断一个素数能被几个9整除。 首先,我们需要明确素数的定义:素数是大于1,且只能被1和自身整除的整数。 下面将分别介绍三种实现方法,每种方法附上解题思路、实现代码、以及优缺点。最后,将对这三种方法进行总结…...
Thymeleaf 内联语法使用教程
1 表达式内联 Thymeleaf标准方言允许使用标签属性(th:)来实现很多的功能,但在有些场景之下,需要将表达式直接写入HTML 代码中和CSS代码中及JavaScript代码中【代码和html文件在一起,分能不开,待验证,有验证的朋友可…...
Django学习笔记-实现聊天系统
笔记内容转载自 AcWing 的 Django 框架课讲义,课程链接:AcWing Django 框架课。 CONTENTS 1. 实现聊天系统前端界面2. 实现后端同步函数 1. 实现聊天系统前端界面 聊天系统整体可以分为两部分:输入框与历史记录。 我们需要先修改一下之前代…...
C++转换函数
什么是转换函数? C转换函数是一种特殊的成员函数,用于将一个类的对象转换为另一个类型。它是通过在类中定义特定的函数来实现的。 转换函数的用途: 类型转换:转换函数可以将一个类的对象从一种类型转换为另一种类型。这样可以方便地在不同…...
Spring Boot中的@Controller使用教程
一 Controller使用方法,如下所示: Controller是SpringBoot里最基本的组件,他的作用是把用户提交来的请求通过对URL的匹配,分配个不同的接收器,再进行处理,然后向用户返回结果。下面通过本文给大家介绍Spr…...
【17】c++设计模式——>原型模式
原型模式的定义 c中的原型模式(Prototype Pattern)是一种创建型设计模式,其目的是通过复制(克隆)已有对象来创建新的对象,而不需要显示的使用构造函数创建对象,原型模式适用于创建复杂对象时&a…...
金三银四好像消失了,IT行业何时复苏!
文章目录 1. 宏观经济形势2. 技术发展趋势3. 教育与培训4. 远程工作和自由职业5. 行业需求和公司招聘计划结论 🎉欢迎来到Java面试技巧专栏~金三银四好像消失了,IT行业何时复苏! ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页&…...
PDF文件超出上传大小?三分钟学会PDF压缩
PDF作为一种流行的文档格式,被广泛用于各种场合,然而有时候PDF文件的大小超出了上传限制,这时候我们就需要采取一些措施来减小PDF文件的大小,下面就给大家分享几个方法,一起来学习下吧~ 方法一:嗨格式压缩大…...
java入坑之国际化编程
一、字符编码 1.1概述 字符编码 --字符:0,a,我,①,,… --计算机只用0和1,1bit(0或者1) --ASCIL码(American Standard Code for Information Interchange) 美国信息交换标准代码,奠定计算机编码基础用一个字节(1Byte8b…...
Kafka客户端核心参数详解
这一部分主要是从客户端使用的角度来理解 Kakfa 的重要机制。重点依然是要建立自己脑海中的 Kafka 消费模型。Kafka 的 HighLevel API 使用是非常简单的,所以梳理模型时也要尽量简单化,主线清晰,细节慢慢扩展。 一、从基础的客户端说起 Kaf…...
踩大坑ssh免密登录详细讲解
目 录 问题背景 环境说明 免密登录流程说明 1.首先要在对应的用户主机名的情况下生成密钥对,在A服务器执行 2.将A服务器d公钥拷贝到B服务器对应的位置 3.在A服务器访问B服务器 免密登录流程 0.用户说明 1.目前现状演示 2.删除B服务器.ssh 文件夹下面的…...
操作系统八股
1、请你介绍一下死锁,产生的必要条件,产生的原因,怎么预防死锁 1、死锁 两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处…...
Hudi SQL DDL
本文介绍Hudi在 Spark 和 Flink 中使用SQL创建和更改表的支持。 1.Spark SQL 创建hudi表 1.1 创建非分区表 使用标准CREATE TABLE语法创建表,该语法支持分区和传递表属性。 CREATE TABLE [IF NOT EXISTS] [db_name.]table_name[(col_name data_type [COMMENT col_co…...
gin 框架的 JSON Render
gin 框架的 JSON Render gin 框架默认提供了很多的渲染器,开箱即用,非常方便,特别是开发 Restful 接口。不过它提供了好多种不同的 JSON Render,那么它们的区别是什么呢? // JSON contains the given interface obje…...
《Dataset Condensation with Differentiable Siamese Augmentation》
《Dataset Condensation with Differentiable Siamese Augmentation》 在本文中,我们专注于将大型训练集压缩成显著较小的合成集,这些合成集可以用于从头开始训练深度神经网络,性能下降最小。受最近的训练集合成方法的启发,我们提…...
多普勒频率相关内容介绍
图1 多普勒效应 1、径向速度 径向速度是作用于雷达或远离雷达的速度的一部分。 图2 不同的速度 2、喷气发动机调制 JEM是涡轮机的压缩机叶片的旋转的多普勒频率。 3、多普勒困境 最大无模糊范围需要尽可能低的PRF; 最大无模糊速度需要尽可能高的PRF;…...
win10睡眠快捷方式
新建快捷方式 如下图 内容如下 rundll32.exe powrprof.dll,SetSuspendState 0,1,0 下一步 点击完成即可。 特此记录 anlog 2023年10月6日...
C++中的static和extern关键字
1 声明和定义 声明就是告诉编译器有这个东西的存在,而定义则是这个东西的实现。 对于变量来说,声明就是告诉编译器存在这个名称的变量,定义则是给这个变量分配内存并赋值: // 变量声明,声明时不能赋值,如…...
JAVA经典百题之找完数
题目:一个数如果恰好等于它的因子之和,这个数就称为"完数"。例如61+2+3.编程找出1000以内的所有完数。 程序分析 首先,我们需要编写一个程序来找出1000以内的所有完数。"完数"是指一个数等于它的…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
python基础语法Ⅰ
python基础语法Ⅰ 常量和表达式变量是什么变量的语法1.定义变量使用变量 变量的类型1.整数2.浮点数(小数)3.字符串4.布尔5.其他 动态类型特征注释注释是什么注释的语法1.行注释2.文档字符串 注释的规范 常量和表达式 我们可以把python当作一个计算器,来进行一些算术…...
