归并排序求逆序数的个数
作者:互联网
描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。
比如 1 3 2 的逆序数就是1。
格式
输入格式
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=100000)
随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。
数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
在这里我们要求逆序数如:1 3 5 2 4
【3 5 2】2是3的逆序数,2也是5的逆序数
【5 2 4】4是5的逆序数。
因此这组数的逆序数的个数为3.
在这里,我们选择用归并排序解决。
其实归并就是分治思想的一种,将一组数据分割成一个个子序列进行排序处理,从而来完成整体的排序处理。
【1 ,3 ,5 ,2, 4】
【1, 3,5】|【2, 4】
【1,3】【5】|【2】【4】
【1】【3】 【5】 【2】 【4】
这样便拆分成最小段了,然后在进行排序组合
【1 3】【5】【2 4】
【1 3 5】【2 4】
【1 2 3 4 5】
需要注意的是求逆序数时,如果后段的数据a[j] 比前面段的数据a[i]小的话那逆序数的个数就是从(i,mid)这些数全部都为逆序数,所以累加器就要加上mid-i+1;
可能有些小伙伴会有些疑问为什么不是从i到j(i,j)的范围全是逆序数呢?
因为啊,当前数据段都经过不断分割的子序列排成了有顺序的序列了,从mid到j-1的数据都是升序(有顺序的)所以对于j来说前面(mid,j-1)的数据对于a[j]来说不构成逆序。
就如【1, 3,5】和【2,4】两段有序的数据2为3,5的逆序
4是5的逆序,就只有5一个数,而它前面的数2对于它来说不是逆序。
只需要Mid-i+1,而不是j-i;
下面看源代码:
#include<stdio.h>
long long s;
int sort(int *a,int *b,int left,int mid,int right);
void fun(int *a,int *b,int left,int right)
{
int i,j,mid;
if(left==right)
return ;
mid=(left+right)/2; //将数组从中间切割
fun(a,b,left,mid); //将前半部分数据进行处理,包括mid
fun(a,b,mid+1,right); //将后半部分数据处理
sort(a,b,left,mid,right); //分割完成就对当前段的数据进行归并排序
}
int sort(int *a,int *b,int left,int mid,int right)
{
int i,j,k=left;
i=left,j=mid+1;
while(i<=mid&&j<=right) //归并排序,哪个小就将数据插入到b数组
{
if(a[i]<=a[j])
b[k++]=a[i++];
else
{
b[k++]=a[j++]; //这里说明数据是无序的,就可以累积逆序数据的个数(对于后段的a[j]来说从i到mid的数都是逆序数)
s+=mid-i+1; //累加从i到mid的逆序数的个数
}
}
while(i<=mid) //如果前段数据还没有数据没有插入进b就循环插入
b[k++]=a[i++];
while(j<=right) //将后段未插入的数据插入进b数组
b[k++]=a[j++];
for(i=left; i<=right; i++) //因为a数组中的数据全部有序保留在b数组,现在将有序的数据copy到a数组,范围是同样是(left,right)
a[i]=b[i];
return 0;
}
int main()
{
int a[1000000];
int b[1000000];
//b数组是用来将原数组a排序的数据暂存,下标与原数组a下标一一对应,可以理解为空间重叠
int i,j,n,t;
scanf("%d",&t);
while(t--)
{
s=0;
scanf("%d",&n);
for(i=0; i<n; i++)
scanf("%lld",&a[i]);
fun(a,b,0,n-1);
printf("%lld\n",s); //输出逆序数的个数
}
}
标签:求逆,right,int,mid,逆序,归并,序数,left 来源: https://blog.csdn.net/qq_50629351/article/details/111674672