其他分享
首页 > 其他分享> > 归并排序求逆序数的个数

归并排序求逆序数的个数

作者:互联网

描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

现在,给你一个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