归并排序解决LeetCode翻转对问题
1. 问题背景与核心挑战LeetCode 493题翻转对Reverse Pairs是算法练习中的一道经典难题要求统计数组中满足i j且nums[i] 2*nums[j]的元素对数。这个问题看似简单但直接使用双重循环的暴力解法时间复杂度为O(n²)在数据量较大时如10^5级别会超时。我在实际刷题和面试准备过程中发现这道题考察的核心是分治思想与归并排序的灵活运用。相比单纯的排序问题它需要我们在归并过程中同步完成特定条件的统计这对理解算法本质提出了更高要求。2. 解法思路与技术选型2.1 暴力解法的局限性最直观的解法是两层循环遍历所有(i,j)组合int count 0; for(int i0; inums.length; i){ for(int ji1; jnums.length; j){ if(nums[i] 2L*nums[j]) count; } } return count;当n5×10^4时操作次数将达到25亿次远超一般OJ系统1秒内能处理的10^8次操作限制。2.2 分治与归并排序的优势归并排序天然具有分治特性将数组分成两半分别处理分治合并两个有序子数组时进行特定统计时间复杂度优化到O(n log n)关键突破点在于在合并两个有序子数组前可以高效统计跨子数组的翻转对数量。因为左右子数组已经各自有序可以利用这个性质通过双指针技巧在O(n)时间内完成统计。3. 归并排序解法实现细节3.1 Java实现框架public int reversePairs(int[] nums) { return mergeSort(nums, 0, nums.length-1); } private int mergeSort(int[] nums, int left, int right){ if(left right) return 0; int mid left (right-left)/2; int count mergeSort(nums, left, mid) mergeSort(nums, mid1, right); count merge(nums, left, mid, right); return count; }3.2 关键统计逻辑实现private int merge(int[] nums, int left, int mid, int right){ // 统计翻转对 int i left, j mid1; int count 0; while(i mid j right){ if(nums[i] 2L * nums[j]){ count mid - i 1; j; }else{ i; } } // 标准归并排序合并过程 int[] temp new int[right-left1]; // ...省略合并代码... return count; }注意必须使用2L强制转换为long类型避免大数相乘导致的整数溢出问题。这是实际编码中常见的坑点。4. 树状数组解法对比分析4.1 离散化处理由于原始数值范围可能很大如[-2^31, 2^31-1]需要先对数组进行离散化收集所有nums[i]和2*nums[i]1确保严格大于排序后去重建立值到排名的映射4.2 树状数组操作// 离散化后的实现 public int reversePairs(int[] nums) { // 离散化代码省略... BIT bit new BIT(discretized.size()); int res 0; for(int inums.length-1; i0; i--){ int val discretized.get(nums[i]); res bit.query(lowerBound(discretized, 2L*nums[i]1)); bit.update(val, 1); } return res; }4.3 性能对比方法时间复杂度空间复杂度编码复杂度归并排序O(n log n)O(n)中等树状数组O(n log n)O(n)较高暴力解法O(n²)O(1)简单归并排序版本在实际面试中更受青睐因为不需要处理离散化的边缘情况代码结构更清晰直观空间使用更可控5. 常见错误与调试技巧5.1 整数溢出问题错误示例if(nums[i] 2 * nums[j]) // 当nums[j]1e9时会溢出正确写法if(nums[i] 2L * nums[j]) // 使用long类型5.2 统计时机错误必须在合并两个有序数组前完成统计如果在合并后才统计会漏掉跨子数组的翻转对。5.3 边界条件处理测试用例应包括空数组全相同元素数组最大/最小整数值完全正序/逆序数组6. 算法扩展与变种6.1 CDQ分治解法CDQ分治是处理三维偏序问题的利器虽然本题是二维偏序但可以用其思想将每个元素视为(i, nums[i])的二元组第一维按i排序天然满足ij第二维用归并处理nums[i]2*nums[j]6.2 实际工程应用类似算法可用于金融交易系统中的异常交易检测基因组序列比对中的反转位点统计版本控制系统中的代码变更影响分析7. 性能优化实践7.1 归并排序的空间优化可以复用临时数组而非每次新建// 类成员变量 private int[] temp; // 初始化时分配一次 temp new int[nums.length];7.2 提前终止优化当左子数组最小值已经2*右子数组最大值时所有左子数组元素都满足条件if(nums[left] 2L * nums[right]){ count (mid-left1)*(right-mid); // 快速合并剩余元素... }8. 不同语言实现要点8.1 C实现注意使用vector代替原生数组更安全注意iterator的使用范围int mergeSort(vectorint nums, int left, int right){ if(left right) return 0; int mid left (right-left)/2; int count mergeSort(nums, left, mid) mergeSort(nums, mid1, right); // 统计逻辑 int i left, j mid1; while(i mid j right){ if(nums[i] 2LL * nums[j]){ count mid - i 1; j; }else{ i; } } // ...合并逻辑 return count; }8.2 Python实现特点利用切片简化代码注意整数自动转为long的特性def reversePairs(nums): def merge_sort(l, r): if l r: return 0 mid (l r) // 2 count merge_sort(l, mid) merge_sort(mid1, r) # 统计逻辑 j mid 1 for i in range(l, mid1): while j r and nums[i] 2 * nums[j]: j 1 count j - (mid 1) # 合并 nums[l:r1] sorted(nums[l:r1]) return count return merge_sort(0, len(nums)-1)9. 测试用例设计策略完整的测试应包含以下场景常规测试Input: [1,3,2,3,1] Output: 2边界测试Input: [2147483647,2147483647,2147483647] // MAX_INT Output: 0性能测试Input: [10000000,9999999,...,1] // 1e5个逆序元素 Expected: 在1秒内完成特殊值测试Input: [] // 空数组 Output: 010. 实际编码中的经验总结调试技巧在归并过程中打印子数组状态可视化统计过程System.out.printf(Processing [%d,%d] and [%d,%d]\n, left, mid, mid1, right);性能分析使用JMH进行微基准测试比较不同实现的吞吐量Benchmark public void testMergeSortSolution(Blackhole bh) { bh.consume(solution.reversePairs(testData)); }代码风格将统计逻辑与合并逻辑分离提高可读性private int countPairs(int[] nums, int left, int mid, int right){ // 纯统计逻辑 } private void merge(int[] nums, int left, int mid, int right){ // 纯合并逻辑 }扩展思考如果条件改为nums[i] 3*nums[j]算法结构是否变化实际上只需要修改比较条件整体框架保持不变。

相关新闻

电力数字孪生与AI智能体融合:从可视化到自主决策的实践路径

电力数字孪生与AI智能体融合:从可视化到自主决策的实践路径

1. 从“看”到“干”:电力行业智能化的真实困境与破局点最近和几个在电网、发电集团做数字化项目的朋友聊天,发现一个挺有意思的现象:大家会议室里的大屏越来越炫酷,三维模型、实时数据流、设备状态渲染得跟科幻电影似的&#xff…

2026/11/6 15:47:05 阅读更多 →
SpringBoot+Vue全栈汉服租赁系统设计与实现

SpringBoot+Vue全栈汉服租赁系统设计与实现

1. 项目背景与核心价值汉服文化在高校学生群体中的流行度近年来持续攀升,但实体租赁门店覆盖有限且价格偏高。这个基于SpringBootVue的全栈项目正是为解决这一痛点而生,它实现了从服装展示、在线预约到门店管理的完整业务流程。作为Java Web方向的毕业设…

2026/11/6 15:47:05 阅读更多 →
Unity MCP完全指南:构建上下文感知的AI游戏开发助手

Unity MCP完全指南:构建上下文感知的AI游戏开发助手

1. 项目概述:为什么Unity开发者需要MCP?如果你在Unity里折腾过AI助手,大概率有过这种体验:你问它“为什么我的角色卡在墙里了?”,它可能会给你一段语法完美、逻辑通顺的C#代码,教你如何检测碰撞…

2026/10/25 1:49:04 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/6 15:21:35 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/6 9:29:27 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/5 12:28:01 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/5 12:28:02 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/6 8:08:42 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/5 19:38:59 阅读更多 →

日新闻

周新闻

月新闻