归并排序及求逆序对数
什么是归并排序?归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
当我们看完这段话是好像既熟悉又陌生......
但是接着往下看,你就会发现一张图:
似乎顿时豁然开朗了起来......
归并排序不就是二分么(不会的同鞋去学习一下二分)
所谓的归并排序其实就是两个步骤:1.递归二分序列2.合并序列
其中第二点合并序列尤为重要!!
怎么去合并呢?
第一步:申请空间temp,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针i,j,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
第四步:将另一序列剩下的所有元素直接复制到合并序列尾
是不是还是有一点懵.....
解释:简单的来说就是依次比较,小的元素放入中间数组temp中,当某一区间内的所有值都放入temp数组中去的时候,就将剩下区间没有进行比较的元素直接合并到temp数组中。
余下的自动补齐temp即10放入temp中。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 1000001
int a[N];
int b[N];
void merge(int l,int mid,int r){
int i=l,j=mid+1,p=1;
while(i<=mid&&j<=r){
if(a[i]>a[j]){
b[p]=a[j];
p++;
j++;
}
else{
b[p]=a[i];
i++;
p++;
}
}
if(i>mid){
for(int k=p;j<=r;j++,k++){
b[k]=a[j];
}
}
else if(j>r){
for(int k=p;i<=mid;i++,k++){
b[k]=a[i];
}
}
for(int k=l,q=1;k<=r;k++,q++)a[k]=b[q];
}
void divide(int l,int r){
if(l==r)return;
int mid=(r+l)/2;
divide(l,mid);
divide(mid+1,r);
merge(l,mid,r);
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
divide(1,n);
for(int i=1;i<=n;i++)cout<<a[i]<<” ”;
return 0;
}
扩展:
归并算法还可以计算出逆序对数
什么是逆序对?
逆序对是指在排列中某一对元素的先后次序与标准次序不同时,就说他构成一个逆序。例如(12534)中有<5,3>,<5,4>两个逆序对。
什么是逆序对数?
一个排列中所有逆序的总数。
归并算法怎么快速求逆序对数?
常规的暴力解法时间复杂度为O(),然而运用归并排序能够将时间复杂度降为O(nlogn),当然还有树状数组的解法也是O(nlogn)
题目:逆序对 - 洛谷
思路如下:
归并算法:
同样是以上步骤,递归下来,然后是“并”操作,这里有一点不同的是,以j为中心,依次遍历比较a[j],如果a[j]>a[i],则记录数据(ans+=mid-i+1),等右字树的遍历完后,完成“并”操作。
例如
对于a[j]=5来说,左子树会一直遍历到a[i]=6,此时可以构成逆序对,逆序对数为mid-i+1即4-3+1=2。为什么是mid-i+1呢??
嗯..
因为归并排序中左子树和右字树中的序列一定是已经排列完的序列,比较以a[j]为第二个元素时此区间拥有多少逆序对数。即找到有多少个这样的<X,a[j]>。当第一次找到一个比a[j]大的数,那么以后的数也一定比a[j]要大(原因如上标红字体)此时逆序对数为mid-i+1;
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 1000001
int a[N];
int b[N];
int cnt=0;
void merge(int l,int mid,int r){
int i=l,j=mid+1,p=1;
while(i<=mid&&j<=r){
if(a[i]>a[j]){
cnt+=mid-i+1;
b[p]=a[j];
p++;
j++;
}
else{
b[p]=a[i];
i++;
p++;
}
}
if(i>mid){
for(int k=p;j<=r;j++,k++){
b[k]=a[j];
}
}
else if(j>r){
for(int k=p;i<=mid;i++,k++){
b[k]=a[i];
}
}
for(int k=l,q=1;k<=r;k++,q++)a[k]=b[q];
}
void divide(int l,int r){
if(l==r)return;
int mid=(r+l)/2;
divide(l,mid);
divide(mid+1,r);
merge(l,mid,r);
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
divide(1,n);
cout<<cnt;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!