1.算法标签:归并排序
2.题目描述:
给定一个长度为 n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i个和第 j个元素,如果满足 i<j且 a[i]>a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000,
数列中的元素的取值范围 [1,1e9]。
输入样例:
6
2 3 4 5 6 1
输出样例:
5
3.思路分析:
将区间一分为二,第一种情况 :i 和 j都在同一边的时候,左半边的逆序对数量和右半边的逆序对数量都可以使用递归函数求解(把左半边、右半边都看成一个“新”的区间),第二种情况:i 和 j不在同一边时,使用归并排序,若q[ i ]>q[ j ](此处指的是:左半边第一个 i 和右半边第一个 j 满足q[ i ]>q[ j ])则左半边中所有q[ i ]之后的数都比q[ j ]大,也就是说它之后的数都满足题目条件,左半边q[ i ]之后的数的数目是mid-i+1。最后的答案就是左半边内部逆序对的数量+右半边内部逆序对的数量+第二种情况(跨越左右半边的)。
4.代码:
#include<iostream>
using namespace std;
const int N=1e5+10;
typedef long long LL;
int n;
int q[N];
int tem[N];
LL merge_sort(int q[],int l,int r)//l是左边界 r是右边界
{
if(l>=r)return 0;//如果输入的左右边界不符合条件(没有元素或者左边界比右边界大)就返回0(没有逆序对)
int mid=(l+r)>>1;
int i=l,j=mid+1;
int k=0;
LL res=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);//左右部分内部逆序对的数量可以使用函数调用,原因见思路分析 此外注意,这时候左右两个半边已经排好序了
//归并排序解决第二种情况
while(i<=mid&&j<=r)//当左边没有到中间边界时以及右边没到右边界时就执行循环体
{
if(q[i]<=q[j])tem[k++]=q[i++];//使用数组tem[]保存排好序的总数组 (分开的左右半边合起来成一个整体都有序的数组)
else
{
res+=mid-i+1;//左半边的第一个 i 以及右半边的第一个 j 满足q[i]>q[j]时
tem[k++]=q[j++];
}
}
//接下来是扫尾工作
while(i<=mid)tem[k++]=q[i++];//当左半边没到中间边界时,把剩下的部分复制给tem[]
while(j<=r)tem[k++]=q[j++];//类比上一行
for(int i=0,j=1;j<=r;++i,++j)q[j]=tem[i];//将排好序的tem[]赋值给原数组q[],如此操作让之后的函数调用结果都是正确的
return res;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)scanf("%d",&q[i]);
cout<<merge_sort(q,0,n-1);
return 0;
}