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

归并延拓:LeetCode归并排序逆序对问题

前言

欢迎来到我的算法探索博客,在这里,我将通过解析精选的LeetCode题目,与您分享深刻的解题思路、多元化的解决方案以及宝贵的实战经验,旨在帮助每一位读者提升编程技能,领略算法之美。
👉更多高频有趣LeetCode算法题

归并排序(Merge Sort) 是一种经典的分治算法,核心思想是将一个数组分解成更小的子数组,然后再将这些子数组合并成有序的数组。归并排序的步骤如下:

  • 分解: 将待排序的数组不断分割,直到每个子数组只有一个元素。
  • 合并: 从两个已排序的子数组开始,逐一比较元素并将它们合并成一个新的有序数组。

归并排序的时间复杂度为 𝑂(𝑛log(𝑛)),它是一种稳定的排序算法,适用于大规模数据的排序。由于其分治的特性,它的空间复杂度为 𝑂(𝑛),需要额外的存储空间。

虽然归并排序的初衷是排序,但它在处理与排序相关的其他问题时也非常有用。

⚠️注意:归并排序具体实现原理及代码本篇文章不做讲解,默认阅读者已经掌握归并排序并能熟练默写代码。一定要有排序算法基础才能继续往下哦! 归并排序具体实现原理请看:👉这里

原理:归并排序

本篇文章不做详细讲解。
归并排序具体实现原理请看:👉这里

实战:经典例题讲解

LCR 170.交易逆序对的总数

🌵题目描述

在这里插入图片描述

🌼核心思路

其实刚拿到这个题呢,最容易想到的是暴力解法两层for循环

使用两层 for 循环枚举所有的数对,逐一判断是否构成逆序关系。

public class Solution {public int reversePairs(int[] nums) {int res = 0;int n = nums.length;for (int i = 0; i < n - 1; i++) {for (int j = i + 1; j < n; j++) {if (nums[i] > nums[j]) {res++;}}}return res;}
}

提交代码发现超时,如果就这么简单做完也不可能打上困难的标签了😅

接下来看一下比较优雅的做法 (当然也可以用树状数组解决逆序数问题,本篇文章不做讲解)

利用归并排序来计算逆序对是一种经典且高效的做法,核心思想在于利用归并过程中的有序性来快速统计逆序对。在归并排序中,递归地将数组分为左右两部分,而在合并这两部分时,我们可以通过比较元素的大小来判断逆序对的数量。

具体而言,逆序对的计算可以分为三个部分:

  1. 左侧区间内的逆序对。
  2. 右侧区间内的逆序对。
  3. 横跨左右区间的逆序对。

在分割过程中不做任何操作,而是在合并阶段,通过数组的部分有序性,我们能够迅速计算出跨区间的逆序对。尤其是在合并过程中,每当一个左侧元素大于右侧元素时,就能够确定这个左侧元素与右侧区间中剩下的所有元素构成逆序对。因此,排序的过程非常关键,因为只有通过排序,才能利用元素之间的顺序关系有效地计算逆序对,并避免重复计算。

所以就会有两种写法了:

1、计算第1个子区间右侧有多少个数比他小,计算逆序对的个数;
2、计算第2个子区间左侧有多少个数比他大,计算逆序对的个数。

在这里插入图片描述
在这里插入图片描述

注意:两者不能同时计算,否则会计算重复。

🌿代码实现

Java

第一版:计算右侧有多少个数比他小

class Solution {int[] record, temp;public int reversePairs(int[] record) {int n = record.length;temp = new int[n];if (n < 2)return 0;return reversePairs(record, 0, n - 1, temp);}public int reversePairs(int[] record, int left, int right, int[] temp) {if (left >= right)return 0;int mid = left + (right - left) / 2;int leftPairs = reversePairs(record, left, mid, temp);int rightPairs = reversePairs(record, mid + 1, right, temp);// 小优化if (record[mid] < record[mid + 1]) {return leftPairs + rightPairs;}int mergePairs = merge(record, left, mid, right, temp);return leftPairs + rightPairs + mergePairs;}private int merge(int[] a, int left, int mid, int right, int[] temp) {// 定义三个指针int p1 = left;int p2 = mid + 1;int p3 = 0; // 记录辅助数组中的索引int res = 0;while (p1 <= mid && p2 <= right) {if (a[p1] <= a[p2]) {temp[p3++] = a[p1++];res += p2 - (mid + 1);} else {temp[p3++] = a[p2++];}}// 将左边剩余的数据填充到辅助数组中while (p1 <= mid) {temp[p3++] = a[p1++];// 此时p2 = right + 1 所以(right + 1) - (mid + 1)res += right - mid;}// 将右边剩余的数据填充到辅助数组中while (p2 <= right) {temp[p3++] = a[p2++];}// 把临时数组中的数据拷贝到原数组中int t = 0;while (left <= right) {a[left++] = temp[t++];}return res;}
}

第二版:计算左侧有多少个数比他大

class Solution {int[] record, temp;public int reversePairs(int[] record) {int n = record.length;temp = new int[n];if (n < 2)return 0;return reversePairs(record, 0, n - 1, temp);}public int reversePairs(int[] record, int left, int right, int[] temp) {if (left >= right)return 0;int mid = left + (right - left) / 2;int leftPairs = reversePairs(record, left, mid, temp);int rightPairs = reversePairs(record, mid + 1, right, temp);// 小优化if (record[mid] < record[mid + 1]) {return leftPairs + rightPairs;}int mergePairs = merge(record, left, mid, right, temp);return leftPairs + rightPairs + mergePairs;}private int merge(int[] a, int left, int mid, int right, int[] temp) {// 定义三个指针int p1 = left;int p2 = mid + 1;int p3 = 0; // 记录辅助数组中的索引int res = 0;while (p1 <= mid && p2 <= right) {if (a[p1] <= a[p2]) {temp[p3++] = a[p1++];} else {temp[p3++] = a[p2++];res += mid + 1 - p1;}}// 将左边剩余的数据填充到辅助数组中while (p1 <= mid) {temp[p3++] = a[p1++];}// 将右边剩余的数据填充到辅助数组中while (p2 <= right) {temp[p3++] = a[p2++];// 此时p1 = mid + 1 所以也可以不写res// res += mid + 1 - p1;}// 把临时数组中的数据拷贝到原数组中int t = 0;while (left <= right) {a[left++] = temp[t++];}return res;}
}
Python

第一版:计算右侧有多少个数比他小

class Solution(object):def reversePairs(self, record):n = len(record)temp = [0] * nif n < 2:return 0return self.reversePairsHelper(record, 0, n - 1, temp)def reversePairsHelper(self, record, left, right, temp):if left >= right:return 0mid = left + (right - left) // 2leftPairs = self.reversePairsHelper(record, left, mid, temp)rightPairs = self.reversePairsHelper(record, mid + 1, right, temp)# 小优化if record[mid] < record[mid + 1]:return leftPairs + rightPairsmergePairs = self.merge(record, left, mid, right, temp)return leftPairs + rightPairs + mergePairsdef merge(self, record, left, mid, right, temp):p1 = leftp2 = mid + 1p3 = 0res = 0while p1 <= mid and p2 <= right:if record[p1] <= record[p2]:temp[p3] = record[p1]p3 += 1p1 += 1res += p2 - (mid + 1)else:temp[p3] = record[p2]p3 += 1p2 += 1while p1 <= mid:temp[p3] = record[p1]p3 += 1p1 += 1res += right - midwhile p2 <= right:temp[p3] = record[p2]p3 += 1p2 += 1t = 0while left <= right:record[left] = temp[t]left += 1t += 1return res
C++

第一版:计算右侧有多少个数比他小

class Solution {
public:int reversePairs(vector<int>& record) {int n = record.size();vector<int> temp(n);if (n < 2)return 0;return reversePairsHelper(record, 0, n - 1, temp);}private:int reversePairsHelper(vector<int>& record, int left, int right, vector<int>& temp) {if (left >= right)return 0;int mid = left + (right - left) / 2;int leftPairs = reversePairsHelper(record, left, mid, temp);int rightPairs = reversePairsHelper(record, mid + 1, right, temp);// 小优化if (record[mid] < record[mid + 1]) {return leftPairs + rightPairs;}int mergePairs = merge(record, left, mid, right, temp);return leftPairs + rightPairs + mergePairs;}int merge(vector<int>& record, int left, int mid, int right, vector<int>& temp) {int p1 = left;int p2 = mid + 1;int p3 = 0;int res = 0;while (p1 <= mid && p2 <= right) {if (record[p1] <= record[p2]) {temp[p3++] = record[p1++];res += p2 - (mid + 1);} else {temp[p3++] = record[p2++];}}while (p1 <= mid) {temp[p3++] = record[p1++];res += right - mid;}while (p2 <= right) {temp[p3++] = record[p2++];}int t = 0;while (left <= right) {record[left++] = temp[t++];}return res;}
};

结语

如果您渴望探索更多精心挑选的高频LeetCode面试题,以及它们背后的巧妙解法,欢迎您访问我的博客,那里有我精心准备的一系列文章,旨在帮助技术爱好者们提升算法能力与编程技巧。

👉更多高频有趣LeetCode算法题

在我的博客中,每一篇文章都是我对算法世界的一次深入挖掘,不仅包含详尽的题目解析,还有我个人的心得体会、优化思路及实战经验分享。无论是准备面试还是追求技术成长,我相信这些内容都能为您提供宝贵的参考与启发。期待您的光临,让我们共同在技术之路上不断前行!

相关文章:

归并延拓:LeetCode归并排序逆序对问题

前言 欢迎来到我的算法探索博客&#xff0c;在这里&#xff0c;我将通过解析精选的LeetCode题目&#xff0c;与您分享深刻的解题思路、多元化的解决方案以及宝贵的实战经验&#xff0c;旨在帮助每一位读者提升编程技能&#xff0c;领略算法之美。 &#x1f449;更多高频有趣Lee…...

51.WPF应用加图标指南 C#例子 WPF例子

完整步骤&#xff1a; 先使用文心一言生成一个图标如左边使用Windows图片编辑器编辑&#xff0c;去除背景使用正方形&#xff0c;放大图片使图标铺满图片使用格式工程转换为ico格式&#xff0c;分辨率为最大 在资源管理器中右键项目添加ico类型图片到项目里图片属性设置为始终…...

Springboot 注解缓存使用教程

Spring Boot Cache 注解使用教程 Spring Boot 提供了强大的缓存抽象,开发者可以通过注解快速实现缓存功能,从而提高系统性能。本教程将全面介绍 Spring Boot 提供的缓存相关注解及其作用,并结合示例讲解实际应用。 1. 常用缓存注解概览 Spring Boot 缓存提供以下核心注解…...

Python爬虫:从入门到实践

Python爬虫学习资料 Python爬虫学习资料 Python爬虫学习资料 在当今数字化信息爆炸的时代&#xff0c;数据已成为企业和个人发展的重要资产。Python爬虫作为一种高效获取网络数据的工具&#xff0c;正逐渐被广大开发者所熟知和应用。无论是市场调研、学术研究&#xff0c;还是…...

删除字符串中的所有相邻重复项(力扣1047)

这题也是属于栈的经典应用。为什么这样说呢&#xff1f;因为也是让我们删除相邻项。注意这里相邻项的理解&#xff0c;并不仅仅是说最开始的字符串相邻的项。在我们删除了某些相邻项后&#xff0c;会改变字符串&#xff0c;导致原本不相邻的字符变成相邻的&#xff0c;这同样属…...

MYSQL对数据的增删改查

DML 语句 对数据 进行 增、删、改 操作 插入 命令-- 插入值的个数 必须和 字段定义的个数相同 且 顺序 一致 insert into <tableName> values (val ...) ; /* 不推荐使用 */insert into <tableName>(col1 , col2 , ...) values(val1, val2 , ...) ;-- 批量插…...

前端——Html+CSS

目录 CSS引入方式 颜色表达方式 CSS选择器 去掉超链接的下划线 路径表示 行高和首行缩进 常见标签 布局标签 flex布局 表单标签 表单项标签 改变鼠标指针的样式 表格标签 div{ box-sizing: border-box; } CSS引入方式 具体有3种引入方式&#xff0c;语法如下表格所…...

Linux(DISK:raid5、LVM逻辑卷)

赛题拓扑: 题目: DISK 添加4块大小均为10G的虚拟磁盘,配置raid-5磁盘。创建LVM命名为/dev/vg01/lv01,大小为20G,格式化为ext4,挂在到本地目录/webdata,在分区内建立测试空文件disk.txt。[root@storagesrv ~]# yum install mdadm -y [root@storagesrv ~]# mdadm -C -n …...

N个utils(sql)

sql&#xff0c;操作数据库的语言&#xff0c;也可以叫做数据库软件的指令集吧。名字而已&#xff0c;无所谓啦。 本质上&#xff0c;sql并不是java语言内的范畴。但却是企业级开发的范畴。并且我整个文章的一篇逻辑的本质&#xff0c;层的概念&#xff0c;其中一个大的层级就…...

以太网实战AD采集上传上位机——FPGA学习笔记27

一、设计目标 使用FPGA实现AD模块驱动采集模拟电压&#xff0c;通过以太网上传到电脑上位机。 二、框架设计 数据位宽转换模块&#xff08;ad_10bit_to_16bit&#xff09;&#xff1a;为了方便数据传输&#xff0c;数据位宽转换模块实现了将十位的 AD 数据转换成十六位&#…...

Python数据分析案例70——基于神经网络的时间序列预测(滞后性的效果,预测中存在的问题)

背景 这篇文章可以说是基于 现代的一些神经网络的方法去做时间序列预测的一个介绍科普&#xff0c;也可以说是一个各种模型对比的案例&#xff0c;但也会谈一谈自己做了这么久关于神经网络的时间序列预测的论文&#xff0c;其中一些常见的模式及它们存在的问题以及效果&#x…...

vue+高德API搭建前端Echarts图表页面

利用vue搭建Echarts图表页面&#xff0c;在搭建Echarts图表中&#xff0c;如果搭建地理地形图需要准备一些额外的文件&#xff0c;地理json文件和js文件&#xff0c;js文件目前在网上只能找省一级的&#xff0c;json文件有对应的省市县&#xff0c;js文件和json文件对应的也是不…...

提示词工程:解锁AI潜能的关键技术

什么是提示词工程? 提示词工程(Prompt Engineering)是一门新兴的技术领域,专注于如何设计和优化与生成式人工智能的交互提示,以获得最佳的输出结果。它是连接人类意图和AI能力的桥梁,通过精心设计的文本输入来引导AI模型产生准确、相关且有价值的输出。 核心概念 提示(…...

Python制作简易PDF查看工具PDFViewerV1.0

PDFViewer PDF浏览工具&#xff0c;Python自制PDF查看工具&#xff0c;可实现基本翻页浏览功能&#xff0c;其它功能在进一步开发完善当中&#xff0c;如果有想一起开发的朋友&#xff0c;可以留言。本软件完全免费&#xff0c;自由使用。 软件界面简洁&#xff0c;有菜单栏、…...

嵌入式硬件篇---基本组合逻辑电路

文章目录 前言基本逻辑门电路1.与门&#xff08;AND Gate&#xff09;2.或门&#xff08;OR Gate&#xff09;3.非门&#xff08;NOT Gate&#xff09;4.与非门&#xff08;NAND Gate&#xff09;5.或非门&#xff08;NOR Gate&#xff09;6.异或门&#xff08;XOR Gate&#x…...

CSRF攻击XSS攻击

概述 ​在 HTML 中&#xff0c;<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址&#xff0c;而所谓的跨域请求就是指&#xff1a;当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指…...

ARM学习(42)CortexM3/M4 MPU配置

笔者之前学习过CortexR5的MPU配置,现在学习一下CortexM3/M4 MPU配置 1、背景介绍 笔者在工作中遇到NXP MPU在访问异常地址时,就会出现总线挂死,所以需要MPU抓住异常,就需要配置MPU。具体背景情况可以参考ARM学习(41)NXP MCU总线挂死,CPU could not be halted以及无法连…...

opencv3.4 ffmpeg3.4 arm-linux 交叉编译

一些依赖安装&#xff1a; sudo apt-get install pkg-config libgtk2.0-dev libavcodec-dev libavformat-dev libswscale-dev 交叉编译工具链准备&#xff1a;gcc-linaro-6.3.1 1、下载 https://github.com/FFmpeg/FFmpeg 解压后新建目录&#xff1a;Fmpeg-n3.4.13/ffmpeg…...

spring的事物管理的认知

事物 它是一个原子操作要么全部不执行&#xff0c;要么全部执行成功&#xff0c;如果有一个失败也会撤销&#xff0c;它保证用户每一次的操作都是可靠的&#xff0c;即使时出现了错误也不至于破坏数据的完整性 它包含了四种特性&#xff1a; 原子性&#xff1a;保证事物要么…...

麒麟LINUX V10SP3 2401安装ORACLE 12.2.1 runInstaller直接报UNZIP格式不对

好久没有安装ORACLE了&#xff0c;一般都是RHEL上安装得比较多&#xff0c;这不&#xff0c;现在大家都是选择国产操作系统来安装数据库了&#xff0c;以前在龙蜥&#xff0c;欧拉&#xff0c;麒麟上也安装过&#xff0c;都没有问题&#xff0c;想来在麒麟LINUX v10sp3 2401上面…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

适应性Java用于现代 API:REST、GraphQL 和事件驱动

在快速发展的软件开发领域&#xff0c;REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名&#xff0c;不断适应这些现代范式的需求。随着不断发展的生态系统&#xff0c;Java 在现代 API 方…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...