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

算法篇----分治(归并排序)

一、归并排序

归并排序是一种分治算法,其基本思想是将一个大问题分解成若干个规模较小的子问题,递归地解决这些子问题,然后将子问题的解合并起来得到原问题的解。归并排序的效率较高,时间复杂度为O(n log n)。

二、步骤总结

1)归并排序通过递归将数组不断划分为更小的部分,直到每个部分只有一个元素,然后逐步合并        这些有序的部分,最终得到一个完全有序的数组。

2)归并排序的关键步骤是合并两个有序数组,这需要额外的空间(临时数组tmp)来存储合并结         果。

三、与快排的区别

二者都是通过递归来进行排序,但不同的是快排需要选择一个基准点,之后左右开工一起排,而归并排序则是将数组不断划分至最小单元,之后从左向右不断递归排序,可以类比为二叉树的后序遍历,而快排可以类比为前序遍历

四、参考习题

1、 排序数组

912. 排序数组 - 力扣(LeetCode)

这是一个典型的归并排序算法的例题,看一下代码复习一下吧~

class Solution {vector<int> tmp;        //造一个辅助数组,用于全局是会节省一点开销~
public:vector<int> sortArray(vector<int>& nums) {tmp.resize(nums.size());     //把辅助数组的容量给确定一下mergesort(nums,0,nums.size()-1);return nums;}void mergesort(vector<int>& nums,int left,int right){if(left>=right) return;//1.选择中间点划分区间int mid=(left+right)>>1;//[left,mid] [mid+1,right]//2.把左右区间排序mergesort(nums,left,mid);mergesort(nums,mid+1,right);//3.合并两个有序数组int cur1=left,cur2=mid+1,i=0;while(cur1<=mid && cur2<=right)tmp[i++]=nums[cur1]<nums[cur2]?nums[cur1++]:nums[cur2++];  //此举就是比较左右两个区间的数,依次选小的放入辅助数组中//可能有数组没有遍历完,处理一下while(cur1<=mid) tmp[i++]=nums[cur1++];while(cur2<=right) tmp[i++]=nums[cur2++];//4.还原for(int i=left;i<=right;i++)nums[i]=tmp[i-left];}
};

2.交易逆序对的总数

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

解题思路:
如果暴力破解的话思路很简单,但是会超时,所以我们用分治的算法来解决一下,我们可以将数组划分成两段,之后先统计左端的逆序对个数,并进行排序,之后再统计右段的逆序对个数,并进行排序,最后再统计一左一右的逆序对个数。而左右两端的统计是通过不断递归来实现的~

在统计一左一右的逆序对个数时,我们左右两段的数组已经是排成升序的了,如图说是

在cur1左侧的都比cur1小,cur2同理,那么我们只需要找到,当nums[cur1]<=nums[cur2]时,让cur1++,而当nums[cur1]>nums[cur2]时,让ret+=right-cur2+1即可,之后cur2++,继续该操作

class Solution 
{int tmp[50010];
public:int reversePairs(vector<int>& nums) {return mergesort(nums,0,nums.size()-1);}int mergesort(vector<int>& nums,int left,int right){if(left>=right) return 0;int ret=0;//1.找中间点,将数组分为两端int mid=(left+right)>>1;//[left,mid] [mid+1,right]//2.左边个数+排序+右边个数+排序ret+=mergesort(nums,left,mid);ret+=mergesort(nums,mid+1,right);//3.一左一右计数int cur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right) {if(nums[cur1]<=nums[cur2]){tmp[i++]=nums[cur2++];}else{ret+=right-cur2+1;tmp[i++]=nums[cur1++];}}while(cur1<=mid) tmp[i++]=nums[cur1++];while(cur2<=right) tmp[i++]=nums[cur2++];for(int j=left;j<=right;j++){nums[j]=tmp[j-left];}return ret;}
};

3.计算右侧小于当前元素的个数

解题思路:

这道题答题思路跟上一条差不多,但是增加的难的点是如何确定下标,更确切的说是如何保证下标和元素一直保持一一对应,为此我们可以仿照哈希表,在构造一个Index的数组,存放各个的下标,值得注意的是,元素在进行排序时,要记得对下标也进行对应的移动

class Solution 
{vector<int> ret;vector<int> index;//// 记录 nums 中当前元素的原始下标int tmpindex[500010];int tmpnums[500010];
public:vector<int> countSmaller(vector<int>& nums) {int n=nums.size();ret.resize(n);index.resize(n);// 初始化⼀下 index 数组for(int i=0;i<n;i++){index[i]=i;}mergesort(nums,0,nums.size()-1);return ret;}void  mergesort(vector<int>& nums,int left,int right){if(left>=right) return;int mid=(right+left)>>1;mergesort(nums,left,mid);mergesort(nums,mid+1,right);int cur1=left,cur2=mid+1,i=0;while(cur1<=mid&&cur2<=right)  //降序{if(nums[cur1]<=nums[cur2]){tmpnums[i]=nums[cur2];tmpindex[i++]=index[cur2++];}else{ret[index[cur1]]+=right-cur2+1;tmpnums[i]=nums[cur1];tmpindex[i++]=index[cur1++];}}//4.处理剩下的排序过程while(cur1<=mid){tmpnums[i]=nums[cur1];tmpindex[i++]=index[cur1++];}while(cur2<=right){tmpnums[i]=nums[cur2];tmpindex[i++]=index[cur2++];}for(int j=left;j<=right;j++){nums[j]=tmpnums[j-left];index[j]=tmpindex[j-left];}}
};

http://www.dtcms.com/a/330275.html

相关文章:

  • 云电竞盒子对游戏性能有影响吗?
  • 手游业务怎么做防护
  • 智慧城市数字孪生:城市管理的“平行宇宙”
  • 补环境基础(四) Hook插件
  • 黎阳之光立体物业透明管理:开启智慧物业新时代
  • 设计原则之【抽象层次一致性(SLAP)】,方法也分三六九等
  • 安装Win10怎样跳过欢迎界面
  • ant-design a-from-model的校验
  • poetry
  • 《深入解析C++中的Map容器:键值对存储的终极指南》
  • 基于51单片机zigbee的病房呼叫系统
  • Datawhale AI夏令营 「2025全球AI攻防挑战赛-赛道一:图片全要素交互认证-生成赛」的赛事项目实践
  • springboot接口请求参数校验
  • 双椒派E2000D系统盘制作全攻略
  • 在腾讯云CodeBuddy上实现一个AI聊天助手
  • 实盘回测一体的期货策略开发:tqsdk获取历史数据并回测,附python代码
  • java循环分页查询数据,任何把查询到的数据,分批处理,多线程提交到数据库清洗数据
  • 第十二节:粒子系统:海量点渲染
  • 远程办公,如何轻松访问公司内网?出差在外也能远程控制局域网内电脑、外网直接连接到指定端口应用
  • 基于通用优化软件GAMS的数学建模和优化分析(GAMS安装和介绍、GAMS程序编写、GAMS程序调试)
  • Ubuntu22.04轻松安装Qt与OpenCV库
  • 【力扣】面试经典150题总结02-双指针、滑动窗口
  • Kubernetes 网络排错
  • 《嵌入式Linux应用编程(四):Linux Framebuffer图形编程》
  • 数学建模算法-day[17]
  • 【Spring Ai框架】
  • CMake笔记:配置(Configure)、生成(Generate)和构建(Build)
  • 软件架构重构:从混沌到有序的系统性演进
  • 第6节 torch.nn介绍
  • 什么是脏读、幻读、不可重复读?