news 2026/6/23 18:29:45

【算法】分治-归并类题目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【算法】分治-归并类题目

归并数组

类似于分治快排,归并是从底下往上递归排序,快排是先解决当前部分再往下排,两个的顺序是反的~

classSolution{int[]tmp;// 辅助数组publicint[]sortArray(int[]nums){// 分治归并if(nums==null||nums.length==0)returnnull;tmp=newint[nums.length];mergeSort(nums,0,nums.length-1);returnnums;}publicvoidmergeSort(int[]nums,intleft,intright){// 中止条件if(left>=right)return;// 1. 选取中间点intmid=(right+left)/2;// 2. 递归左右区间排序mergeSort(nums,left,mid);mergeSort(nums,mid+1,right);// 3. 排序(双指针)// int[] tmp = new int[right - left + 1];// tmp每次都要new 消耗资源 故放到全局变量中intcur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right)tmp[i++]=nums[cur1]<=nums[cur2]?nums[cur1++]:nums[cur2++];// 细节问题:cur1 或者 cur2 可能没有走到最后// 虽然是两个while 但也只会执行其中一个while(cur1<=mid)tmp[i++]=nums[cur1++];while(cur2<=right)tmp[i++]=nums[cur2++];// 4. 归并合到一起for(intj=left;j<=right;j++)nums[j]=tmp[j-left];// tmp 要从0开始取}}

数组中的逆序对的总数

本质上就是依靠“排序数组 + 元素的相对位置不变”的逻辑,从而优化计算逆序对的策略,使其时间复杂度由单个遍历元素的o ( N 2 ) o(N²)o(N2)->o ( 1 ) o(1)o(1)

思路总结

  1. 最初以暴力枚举开始,必然会超时o ( N o(No(N2 22) )),思考如何优化统计逆序对的逻辑并非单一的枚举每一个元素

第一版

// 这里的嵌套循环和重置 cur2,本质上还是 O(N^2)while(cur1<=mid){while(cur1<=mid&&cur2<=right){if(nums[cur1]>nums[cur2])count++;// 还是在暴力找cur2++;}cur2=mid+1;// 回退指针,复杂度爆炸cur1++;}
  1. 利用数组的单调性批量处理元素之间的大小关系(比如 A > B,那 A 后面比 A 大的肯定也 > B),此时逆序对的数量就能以o ( 1 ) o(1)o(1)​**的时间复杂度计算**

  2. 归并排序(分治)的思想能解决这种问题,不会打乱各自的相对位置,递归会让各自左区间与右区间都是有序的

  3. 优化左右两边[left, mid] [mid + 1, right]​都为有序数组,维护一个辅助数组int[] tmp​ + 双指针int cur1, cur2。这里以递增数组为例,遇到小的元素就往辅助数组上放

  4. 在排序中统计逆序对有两种方式:

// 最终核心 Merge 逻辑while(cur1<=mid&&cur2<=right){if(nums[cur1]<=nums[cur2]){// 没有逆序对的情况// 将小的放进tmp数组中,并移动cur1指针tmp[i++]=nums[cur1++];}else{// 左边大于右边,则左边 cur1 之后的所有数都能和 cur2 构成逆序对count+=(mid-cur1+1);// 👈为了优化这里tmp[i++]=nums[cur2++];// 将小的放进tmp中,移动cur2指针,找下一组逆序对}}
// ⭐从小到大的递减数组版本(只需要更改这里的逻辑,其他代码不变)// [left, mid] [mid + 1, right]// 如果递减的右侧数字比左侧区域的某一数字都要小,那么右边剩下的肯定也比它小while(cur1<=mid&&cur2<=right){if(nums[cur1]<=nums[cur2]){// 没有逆序对的情况// 将大的放进tmp数组中,并移动cur2指针tmp[i++]=nums[cur2++];}else{count+=right-cur2+1;// 递减后的逻辑区域取的是右边的数组区域tmp[i++]=nums[cur1++];// 移动cur1指针,找下一组逆序对}}
  1. 分类讨论:以递增数组为例,每遇到小的元素就将它放到数组tmp上,然后移动对应指针
  2. 剩余元素的处理(边界问题):主循环结束后,通常会有一边还剩下一部分元素,cur1​ 或cur2​ 没走完的情况,需要将剩下的**有序数组**都放到tmp
// 收尾阶段:直接搬运,无需比较,因为子数组已保证有序while(cur1<=mid)tmp[i++]=nums[cur1++];while(cur2<=right)tmp[i++]=nums[cur2++];

细节问题

排序是否破坏相对位置

剩余元素的处理(边界问题)

代码实现

classSolution{intcount=0;int[]tmp;// 辅助数组publicintreversePairs(int[]nums){intn=nums.length;if(n<=1)return0;tmp=newint[n];mergeSort(nums,0,n-1);returncount;}publicvoidmergeSort(int[]nums,intleft,intright){// 中止条件if(left>=right)return;// 取中间intmid=(right+left)/2;// 继续往下递归mergeSort(nums,left,mid);mergeSort(nums,mid+1,right);// 分类讨论// 优化左右两边都为有序数组(利用暂存数组与双指针来排序)————为了优化查找逆序对的逻辑// 如果有序递增的左侧数字都比右侧的某一数字都大,那么左边剩下的肯定也比它大// 此时逆序对的数量就能以o(1)的时间复杂度计算intcur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right){if(nums[cur1]<=nums[cur2]){// 没有逆序对的情况// 将小的放进tmp数组中,并移动cur1指针tmp[i++]=nums[cur1++];}else{// nums[cur1] > nums[cur2]的情况count+=mid-cur1+1;// 👈为了优化这里tmp[i++]=nums[cur2++];// 将小的放进tmp中,移动cur2指针}}// ⭐从小到大的递减数组版本(只需要更改这里的逻辑,其他代码不变)// [left, mid] [mid + 1, right]// 如果递减的右侧数字比左侧区域的某一数字都要小,那么右边剩下的肯定也比它小// while (cur1 <= mid && cur2 <= right) {// if (nums[cur1] <= nums[cur2]) {// // 没有逆序对的情况// // 将大的放进tmp数组中,并移动cur2指针// tmp[i++] = nums[cur2++];// } else {// count += right - cur2 + 1; // 递减后的逻辑区域取的是右边的数组区域// tmp[i++] = nums[cur1++];// }// }// 处理部分未放到tmp的有序数组// 为什么是剩下的数组都是有序的?————从最底层返回的单个数字就是有序的数组// 原本剩下的数字其实也是底下一层返回来的部分有序的数组while(cur1<=mid)tmp[i++]=nums[cur1++];while(cur2<=right)tmp[i++]=nums[cur2++];// 为上一层返回有序数组,注入到nums中for(intj=left;j<=right;j++)// 注意j <= rightnums[j]=tmp[j-left];}}

看到这里希望对你有所帮助,让我们变得更强

未完待续,此博客会保持更新~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 6:36:46

Open-AutoGLM脚本如何做到零故障运行?3个关键编写标准揭晓

第一章&#xff1a;Open-AutoGLM 自定义脚本编写规范在开发基于 Open-AutoGLM 框架的自动化任务时&#xff0c;遵循统一的脚本编写规范是确保代码可读性、可维护性和跨团队协作效率的关键。所有自定义脚本应以模块化结构组织&#xff0c;并严格遵守命名约定与异常处理机制。代码…

作者头像 李华
网站建设 2026/6/22 9:49:22

Open-AutoGLM集成难题全解析:5步打通CI/CD流水线瓶颈

第一章&#xff1a;Open-AutoGLM集成难题全解析&#xff1a;5步打通CI/CD流水线瓶颈在将 Open-AutoGLM 集成至企业级 CI/CD 流水线时&#xff0c;常因模型依赖复杂、构建缓存失效和环境隔离不足导致部署延迟。通过系统化拆解集成路径&#xff0c;可显著提升自动化流程稳定性与交…

作者头像 李华
网站建设 2026/6/16 3:04:13

价值投资中的宏观经济考量:全局视野

价值投资中的宏观经济考量:全局视野 关键词:价值投资、宏观经济分析、投资决策框架、经济周期、行业轮动、资产配置、风险对冲 摘要:本文深入探讨价值投资中宏观经济考量的重要性及其应用方法。文章首先介绍宏观经济分析在价值投资中的核心地位,然后详细解析关键经济指标与…

作者头像 李华
网站建设 2026/6/23 6:31:56

Open-AutoGLM收费模式全解析:5种主流定制开发计费方式及企业选型建议

第一章&#xff1a;Open-AutoGLM企业定制开发收费模式概述Open-AutoGLM作为面向企业级应用的大模型定制平台&#xff0c;提供灵活且透明的收费模式&#xff0c;旨在满足不同规模企业在AI集成过程中的多样化需求。其核心计费机制围绕功能模块、服务等级与资源消耗三个维度构建&a…

作者头像 李华
网站建设 2026/6/23 2:35:25

【大模型开发新范式】:Open-AutoGLM 如何让AI研发效率提升300%?

第一章&#xff1a;Open-AutoGLM 开发文档核心解读Open-AutoGLM 是一个面向自动化自然语言任务的开源框架&#xff0c;旨在简化大语言模型&#xff08;LLM&#xff09;在实际业务场景中的集成与调优流程。其核心设计理念是通过声明式配置驱动模型行为&#xff0c;支持任务编排、…

作者头像 李华