当前位置: 首页 > 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上面…...

OpenClaw入门案例:第一个龙虾智能体程序(Hello World版,复制可运行)

OpenClaw入门案例&#xff1a;第一个龙虾智能体程序&#xff08;Hello World版&#xff0c;复制可运行&#xff09;&#x1f4da; 本章学习目标&#xff1a;深入理解OpenClaw入门案例的核心概念与实践方法&#xff0c;掌握关键技术要点&#xff0c;了解实际应用场景与最佳实践。…...

大模型安全生死线:SITS2026专家披露2024年已验证的7类新型对抗样本绕过手法及防御代码库

第一章&#xff1a;SITS2026专家&#xff1a;大模型对抗攻击防护 2026奇点智能技术大会(https://ml-summit.org) 随着大语言模型在金融、医疗与政务等高敏感场景的深度部署&#xff0c;对抗样本引发的误判、越狱与数据泄露风险已从学术问题演变为现实威胁。SITS2026专家团队基…...

3分钟快速上手!MaaYuan代号鸢如鸢自动化辅助工具终极指南

3分钟快速上手&#xff01;MaaYuan代号鸢如鸢自动化辅助工具终极指南 【免费下载链接】MaaYuan 代号鸢 / 如鸢 一键长草小助手 项目地址: https://gitcode.com/gh_mirrors/ma/MaaYuan 还在为《代号鸢》和《如鸢》的日常任务重复操作而烦恼吗&#xff1f;MaaYuan作为一款…...

WSL2下通过VcXsrv配置xfce4桌面环境的完整指南

1. 为什么要在WSL2中配置xfce4桌面环境 作为一个长期在Windows和Linux双系统间切换的开发者&#xff0c;我深刻理解那种在两个系统间反复重启的痛苦。WSL2的出现彻底改变了这种局面&#xff0c;它让我们能在Windows系统中直接运行原生的Linux环境。但纯命令行操作总有些不便&am…...

Excel数据导入与批处理脚本的完美结合

在日常工作中,我们常常需要处理大量的重复性工作,而这些工作如果能够通过自动化脚本来完成,无疑会极大地提高我们的工作效率。本文将介绍如何利用一个简单的批处理脚本(.bat文件),结合Excel数据,实现自动打开多个账户页面的功能。 问题背景 假设我们有一个Excel文档,…...

手把手教你用Cursor的.cursorrules文件,定制你的专属Python/React开发AI伙伴

用.cursorrules文件打造你的智能编程伙伴&#xff1a;Python/React开发者的终极配置指南 在当今快节奏的软件开发环境中&#xff0c;AI编程助手已经成为提升效率的必备工具。而Cursor作为其中的佼佼者&#xff0c;其真正的威力往往被大多数开发者所低估——通过精心设计的.curs…...

用DDRNet-23-slim在RTX 3060笔记本上搞定细胞图像分割:从数据标注到模型测试的完整避坑记录

在RTX 3060笔记本上实现细胞图像分割&#xff1a;DDRNet-23-slim实战全流程解析 当我在生物实验室第一次看到显微镜下的细胞图像时&#xff0c;立刻被那些复杂的结构吸引了。作为一名刚接触医学图像处理的研究生&#xff0c;我迫切希望能用AI技术自动识别不同类型的细胞。但实验…...

ConvNeXt 系列改进:二次创新 ConvNeXt:结合 RepVGG 结构重参数化,训练多分支、推理单路

关键词:ConvNeXt RepVGG 结构重参数化 推理加速 模型部署 写在前面 2026年的视觉模型赛道呈现出一种有趣的“返璞归真”趋势——在Transformer狂飙数年之后,卷积网络正以全新的姿态回归。这其中,ConvNeXt无疑是纯卷积阵营中最耀眼的明星。从2022年Meta AI首次提出至今,…...

AB测试中的中心极限定理:为什么你的实验结果可以相信?

AB测试中的中心极限定理&#xff1a;为什么你的实验结果可以相信&#xff1f; 在互联网产品的快速迭代中&#xff0c;AB测试已成为决策的黄金标准。但你是否曾疑惑&#xff1a;为什么观察5000个用户的点击率差异&#xff0c;就能 confidently 宣称某个按钮颜色更好&#xff1f;…...

暗黑3技能自动化:从手动挣扎到智能操控的技术跃迁

暗黑3技能自动化&#xff1a;从手动挣扎到智能操控的技术跃迁 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面&#xff0c;可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 在暗黑破坏神3的高层秘境中&#xff0c…...