归并排序原理(洛谷 P1908 逆序对 应用)

【归并排序】 详细解析 & 动图演示 & 逐图解析 & 洛谷P1177【模板】排序 & sort【快速排序】 归并排序动图演示,归并排序图片逐图分析,sort排序【快速排序】,归并排序复杂度分析,例题:洛谷P1177【模板】排序 阅读详情

一.归并排序原理 

递归划分(递归的出口为子区间元素个数为1)

归并排序运用了 分治 的思想,将一个无序的数组按照中点 mid 划分成左右两个区间:

[ left , mid]和[ mid+1,  right ] ,直到区间内元素个数为 1 的时候,此时这个子区间一定是有序的(毕竟只有一个元素)

回溯合并(持续地将左右两个较短的有序数组合并为一个较长的有序数组,直至结束)

最后再把这些区间合并,这样保证了每一步之后的子区间一定有序

如下图是归并排序的简单实现过程

 根据观察可知,形成了深度为 \log_{2}n的递归树,每次要执行n次操作完成子区间的排序,综上,该算法的时间复杂度为 n*log \, n

 二.归并排序的C++代码实现

#include <bits/stdc++.h>
using namespace std;

int nums[] = {7, 3, 2, 4, 6, 1, 5, 0};

// 递归实现的归并排序
void msort(int l, int r) {
	int mid = (l + r) / 2; // 找到中间点
	if (l >= r) return; // 如果区间内只有一个元素,直接返回
	
	msort(l, mid); // 递归排序左半部分
	msort(mid + 1, r); // 递归排序右半部分
	
	int tempSize = r - l + 1; // 计算临时数组的大小
	int temp[tempSize]; // 创建临时数组存放合并后的有序数组
	
	int i = l, j = mid + 1, t = 0; // 初始化指针
	//i 是指向左半区间的指针
	//j 是指向右半区间的指针
	//t 是指向临时数组的指针
	
	// 合并两个有序数组
	while (i <= mid && j <= r) {
		if (nums[i] <= nums[j]) {
			temp[t++] = nums[i++]; // 将较小值复制到临时数组
		} else {
			temp[t++] = nums[j++]; // 将较小值复制到临时数组
		}
	}
	
	// 复制剩余的元素(如果有的话)
	while (i <= mid) temp[t++] = nums[i++];
	while (j <= r) temp[t++] = nums[j++];
	
	// 将临时数组中的元素复制回原数组
	//相当于在nums中拿出来一段排序
	//再将排序好的那一段放回去
	for (int i = 0; i < tempSize; i++) {
		nums[l + i] = temp[i];
	}
}

int main() {
	int len = sizeof(nums) / sizeof(nums[0]); // 计算数组长度
	msort(0, len - 1); // 调用归并排序函数
	
	// 打印排序后的数组
	for (int i = 0; i < len; i++) {
		cout << nums[i] << " ";
	}
	return 0;
}

三.归并排序的原理在题目中应用(洛谷P1908 逆序对)

P1908 逆序对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1908根据归并排序的原理,我们可以发现此算法和求逆序对将有关联

因为当将这些数组在回溯合并的阶段,每一部分在合并成更长的区间前是有序的,所以可以运用这个性质进行求解。

比如这一部分,3 7和2 4都是有序的,对应的指针  i 指向左区间, j 指向右区间,满足 i < j 了,只要再nums[ i ]  > nums [ j ]即可构成一个逆序对。然后 i 指向 3,j 指向 2,此时贡献出了两个逆序对(3和2   7和2),因为3 7是有序的,较小数在前面(3),3可以与右区间构成逆序对,更大的数 7 自然也可以由此得出规律,当 i 所指向的左半边的数大于 j 所指向的右半边的数时,贡献出 (mid - i + 1 )个逆序对(mid是左区间最右侧对应下标,i 是左区间对应的当前元素的下标,+1是表示个数,消除索引偏移),完成这一部分后,将右区间中的 2 存入临时数组中,j 指针指向 4 ,然后进行下一步操作。(这便是归并排序中的回溯合并操作,逐步把最小的数字取出来再合并)

如果对应的不是nums[ i ] >nums[ j ],而是nums[ i ] <= nums[ j ],说明左区间中的最小的数和右区间的数构成不了逆序对,那么需要左区间出来一个更大的数,那么此时 i 指向的这个左区间内的较小的数应该放到临时数组中,i 指针指向左区间的下一位。(我们发现这一步也是把较小的元素存入临时数组,这正是归并排序的操作)

!!!:上述操作是在 i 没超过左区间边界 且 j 没超过右区间边界时进行的(i<=mid&&j<=r)

但是还有时候,当递归划分时,分左右区间,是将一个奇数平分,所以左右区间数不相等,对比之后可能会有剩余,或者就算左右区间数字个数相等时,也会有剩余,此时还需要对剩余的元素进行判断和操作

(依次看 i <=mid 和 j<=r是否成立,因为这个是在i<=mid&&j<=r对应的while循环之后进行的,那么它的作用便是看左右区间内是否有剩下的元素)

AC代码如下

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int Maxn = 5e5;
int n;
ll nums[Maxn+5];
ll b[Maxn+5];
ll ans = 0;
void msort(int l,int r){
	int mid = (l+r)/2;
	if(l==r) return ;
    //只有一个元素了 此时递归结束
	else{
	msort(l,mid);//递归划分左半边
	msort(mid+1,r);//递归划分右半边
	}
	int i = l ,j = mid+1 , t = l;
	//i指向左半边 j指向右半边
	//t代表临时数组的下标
	while(i<=mid && j<=r){
		if(nums[i]>nums[j]){
			ans+=mid-i+1;
			//因为是排好序再比较的,所以如果
			//nums[i]都比nums[j]大,那么左半边所有元素中
			//比nums[j]大的有 mid(左半边总个数)-i(当前元素下标)+1(索引偏移)
			b[t++] = nums[j++];
		}else{
			//b中存入较小元素
			//最后b中的元素都是有序的,再把b中元素复制到nums中
			//这样就对应上面所说的"排好序再比较"
			b[t++] = nums[i++];
		}
	}
	//当左半边或者右半边都比较完了 另一边还有剩余的
	while(i<=mid)//左半边还有剩余
	{
		b[t++] = nums[i++];
	}	
	//右半边还有剩余
	while(j<=r){
		b[t++] = nums[j++];
	}
	//排序后的结果放到nums中
	for(int i =l;i<=r;i++){
		nums[i] = b[i];
	}

	
	
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	//输入
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>nums[i];
	}
	msort(1,n);
	cout<<ans;
	return 0;
}

别再暴力枚举了!用归并排序5分钟搞定洛谷P1908逆序对(附C++代码详解) 本文详细介绍了如何利用归并排序高效解决洛谷P1908逆序对问题,避免暴力枚举的超时风险。通过C++代码详解和原理剖析,帮助读者掌握O(nlogn)时间复杂度的算法技巧,适用于信息学奥赛等编程竞赛场景。 阅读详情

相关推荐

从信息学奥赛到算法实战:归并排序巧解逆序对问题

本文详细解析了如何利用归并排序高效解决逆序对问题,从暴力解法的不足到分治思想的优化,再到代码实现的关键细节和实战演练。通过洛谷P1908的实例,展示了算法在竞赛中的实际应用,并分享了调试技巧和算法思维的延伸应用。

weixin_29275257的博客 200

【洛谷】P1908 逆序对(递归,归并排序)

题目:https://www.luogu.org/problem/P1908 标签: 递归、归并排序 【题解】 数据量很大,普通的比较很容易超时。 题意是比较找出 aia_iai​ > aja_jaj​且 i < j 的有序对,因此只需比较aia_iai​ 和后面的数即可,所以可以用归并排序,在合并的时候比较。 注意: 记录逆序对的数目ans要用long long 型,虽然数据量n...

Samven_7的博客 574

从信息学奥赛到面试刷题:用归并排序搞定逆序对问题(附洛谷P1908 AC代码)

本文深入探讨了归并排序在解决逆序对问题中的高效应用,从信息学奥赛到技术面试的实战场景。通过分析逆序对的数学原理和多种变体问题(如数组小和、翻转对等),提供Python和C++代码实现,并分享工程优化技巧。掌握这一分治统计模式,可系统提升算法竞赛和面试解题能力。

weixin_30696427的博客 304

洛谷1177-归并排序--C++(归并排序)

归并排序的步骤就是先划分,再把两部分进行两两合并,合并的时候就用到了归并排序。归并排序是一个稳定的排序,算法的时间效率是O(nlgn),空间效率是O(n),是一个比较好的排序。 AC代码 #include<iostream> using namespace std; const int N=100010; int a[N]; int n; void merge(int l,int r,int mid){ int tmp[N]={0}; int lpos=l,rpos=mid+1,pos=l;

DongLUOWAN的博客 1394

题解:洛谷 P1908 逆序对

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。个数,表示给定的序列。的序列,需要统计逆序对数量,即满足。关键观察是:逆序对统计可以利用。输出序列中逆序对的数目。的组合,时间复杂度为。#普及- #归并排序。

团爸讲算法 735

告别OpenAI API调用:手把手在本地CPU/GPU上部署fast-whisper中文语音识别模型

本文详细指导如何在本地CPU/GPU上部署fast-whisper中文语音识别模型,摆脱对OpenAI API的依赖。从环境准备、模型获取与优化,到CPU/GPU环境的具体部署步骤和性能优化技巧,全面覆盖本地化语音识别的实现方案,帮助开发者提升效率并保障数据隐私。

weixin_29988907的博客 305

P1908 逆序对 【归并排序】

题目描述 猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。 最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aja_i>a_jai​>aj​ 且 i<ji<ji<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。 Update:数据已加强。 输入格式 第一行

ln2037的博客 791

基于EKF的UWB-IMU融合定位系统在农业机器人中的抗干扰优化与Matlab仿真

本文探讨了基于扩展卡尔曼滤波(EKF)的UWB-IMU传感器融合定位系统在农业机器人中的应用。通过Matlab仿真验证,该系统有效解决了农业环境中UWB信号遮挡和IMU累积误差问题,实现了厘米级高精度定位,显著提升了机器人在复杂场景下的抗干扰能力和导航可靠性。

numpy6sculptor的博客 622

洛谷 P1908 逆序对

洛谷 P1908 逆序对。考察高效排序算法(也许

qq_17807067的博客 902

STM32 SAI接口实战:用CubeMX快速配置多通道音频采集(附DMA优化技巧)

本文详细介绍了如何使用STM32的SAI接口通过CubeMX快速配置多通道音频采集系统,并提供了DMA双缓冲优化技巧。内容涵盖TDM模式配置、时钟同步、DMA优化及常见问题排查,帮助开发者高效实现高速数据采集应用。

weixin_42549037的博客 225

同步整流buck电路图设计:手把手入门必看

从零开始拆解同步整流Buck电路图及其原理,手把手带你画出关键拓扑、选型MOSFET、分析续流路径。重点讲清占空比控制逻辑与体二极管导通损耗的规避方法,让buck电路图及其原理真正落地可调、稳定可靠。

weixin_33308579的博客 995

从洛谷P1908到信息学奥赛:用归并排序手撕逆序对,保姆级代码避坑指南

本文深入解析了归并排序在解决逆序对问题中的应用,特别针对信息学奥赛、洛谷P1908等OJ平台的题目需求。通过详细的代码实现和优化技巧,帮助选手高效统计逆序对,避免常见陷阱,提升算法竞赛表现。

weixin_30527323的博客 462

【Java核心高阶进阶】44-Java8核心新特性

模块十:Java新特性演进 · 第 1 篇Java 8 是 Java 语言历史上最重要的一次发布,它引入的函数式编程范式从根本上改变了 Java 的编码方式。即便到了 JDK 21 的今天,Lambda、Stream、Optional 仍是日常开发的高频工具。本篇将深入这些特性的语法、原理与底层机制,帮助你不仅会用,更知其所以然。

gaochufukanyongren的博客 24

从归并排序到逆序对:一个算法竞赛选手的思维跃迁(附洛谷P1908满分代码)

本文深入探讨了归并排序在算法竞赛中的应用,特别是如何利用其分治特性高效计算逆序对数量。通过洛谷P1908满分代码实例,详细解析了算法实现中的关键细节与常见陷阱,帮助竞赛选手提升计算思维和实战能力。

weixin_30892889的博客 559

飞牛fnNAS搭建web版微信

摘要:本文介绍了一种通过Docker在飞牛NAS上部署微信客户端的方法,实现24小时同步微信聊天记录。具体步骤包括:1)激活飞牛NAS的Docker功能并配置镜像加速源;2)创建专用文件夹并下载docker-wechat镜像;3)设置容器参数并部署微信客户端;4)通过局域网或ZeroTier内网穿透实现远程访问。相比传统Windows/Linux方案,该方法资源占用更低,适合需要长期保存微信聊天记录的用户。

dgnankai的专栏 3330

上下双相机定位贴合(原理到应用,halcon源码详解)

上下双相机定位贴合(原理到应用,halcon源码详解) 一、简介(类型与之前的文章)看你们理解那个文章快 主要讲解上下双相机定位贴合的原理和实现过程,包括各种标定、组合使用及具体的halcon源码实现,适用于的X、Y、Z三轴加一旋转轴系统,如模组组成的多轴系统、Scara四轴机器、六轴机器人(在运行过程中保证几个自由度不动,运行即可) 二、标定介绍 这一小节总结了通用的相机和轴的相对位置模型,并对其标定进行详细讲解 本系统仅使用了九点标定,精度可做到0.05mm 九点标定作用: A、本质就是仿射变换,一个坐

苏州程序大白的博客 9973

洛谷P1908 逆序对【归并排序】

题目链接:P1908 逆序对 程序说明: 归并排序的原理是先分治后合并,分治之后的结果是每个子序列有序,然后依次将两个子序列合并。因为每个子序列都有序,所以不可能出现逆序对。 归并排序默认从小到大排,那么当每两个子序列进行合并操作时,如果位于前面的子序列中的某一个数大于后面子序列中的某数时,则出现了逆序对。设i是指向前一个子序列的指针,j是指向后一个子序列的指针,则当出现逆序对时,一定有a[i]...

cutlery1137的博客 419

SGP4/SDP4详解

**SGP4** (Simplified General Perturbations Version 4) 和 **SDP4** (Simplified Deep-space Perturbations Version 4) 是一组**解析轨道预报模型**,用于预测地球轨道卫星的位置和速度。它们是北美防空司令部(NORAD)维护的**TLE**数据格式的标准配套算法。 简单来说:**TLE是卫星轨道的"身份证",而SGP4/SDP4是解读这张身份证的"说明书"**。

ScilogyHunter的博客 589

线段树/树状数组 | 洛谷P1908 逆序对题解

本文总结了计算逆序对数量的两种高效方法:归并排序和树状数组/线段树。归并排序在排序过程中通过合并阶段统计逆序对,时间复杂度为O(nlogn)。树状数组和线段树则需先离散化数据,通过动态维护和查询区间统计逆序对。树状数组代码更简洁,但线段树适用性更广。文中提供了相关代码实现,并比较了两种数据结构的差异,强调处理离散化和重复元素的技巧。两种方法都能在合理复杂度内解决大规模逆序对问题。

2501_93040725的博客 514

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

【算法笔记:归并排序+逆序对】求逆序对

题目来源: 洛谷P1908 逆序对 WUSTOJ 1850 求逆序对 题目描述: 1003: 求逆序对 给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目。 Input 第一行为n,表示序列长度,接下来的一行包含n个整数(A1,A2,…,An),表示序列中的n个数。 N<=105,Ai<=1055,Ai<=105 Ou...

qq_44684599的博客 1001

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

归并排序+例题

归并排序+例题 归并排序(Merge-Sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 以下图片转载自:简书 图解归并排序 一、图示过程 1、归并排序的过程 2、合并两个有序数组的过...

小小白成长之路 4770

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

四、P1908 逆序对 (树状数组包懂!!!)

两种方法,树状数组极详细

xiaolongmeiya的博客 948

信息学奥赛刷题必备:用归并排序高效求解逆序对(附洛谷P1908满分代码)

本文深入解析了归并排序在信息学奥赛中高效求解逆序对问题的应用,通过洛谷P1908满分代码示例,详细介绍了算法原理、关键实现细节和优化技巧。文章特别强调了数据类型选择和边界条件处理等竞赛实战要点,帮助选手避免常见错误,提升解题效率。

weixin_30530339的博客 377

分治算法:归并排序求解逆序对 | 洛谷P1908

逆序对是归并排序的经典应用。借助分治思想,在归并合并阶段统计跨区间逆序对

Breeze的博客 233

信息学奥赛一本通 1311:【例2.5】求逆序对 | 1237:求排列的逆序数 | OpenJudge NOI 2.4 7622:求排列的逆序数 | 洛谷 P1908 逆序对

【题目链接】 ybt 1311:【例2.5】求逆序对 ybt 1237:求排列的逆序数 OpenJudge NOI 2.4 7622:求排列的逆序数 洛谷 P1908 逆序对 ybt 1311,1237 OpenJudge 2.4 7622几题输入数据的最大个数为10510^5105 洛谷 P1908 输入数据的最大个数为5∗1055*10^55∗105 【题目考点】 1. 归并排序 求逆序对 【君义精讲】排序算法 【解题思路】 如果直接写两层循环寻找逆序对,时间复杂度是O(n2)O(n^2)O(n2),该

君义NOIP的博客 5248

别再暴力枚举了!用归并排序5分钟搞定洛谷P1908逆序对(附完整C++代码)

本文详细介绍了如何利用归并排序高效解决洛谷P1908逆序对问题,避免暴力枚举的超时风险。通过分治策略,在O(nlogn)时间内统计逆序对数量,并提供完整C++代码实现及关键注释,帮助算法竞赛选手快速掌握这一经典解法。

weixin_30565199的博客 348
上一篇: P3374 【模板】树状数组 1
下一篇: 通讯录管理系统(黑马程序员C++项目)
ByteMaster_
博客等级 码龄3年 328粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值