C1-A-求逆序对个数

C1-A-求逆序对个数


时间限制: 300 ms 内存限制: 65536 kb

题面

题目链接
给定一个整数序列,求逆序对的个数

输入
第一个数为序列长度n
接下来一行,n个整数,保证在int范围内

输出
输出一行,逆序对的个数

输入样例

5
5 4 3 2 1

输出样例

10

数据范围
1≤n≤105

解题思路

思路

一般思路是对于每一个数据都向后搜索比它大的数,但这种方法的时间复杂度是o(n2)
一般思路

这样的比较方式有没有让你联想到选择排序?选确定要排的元素的位置,再往后逐个寻找该选择哪个元素和选定元素的位置互换。只不过在求逆序对的时候我们并没有将原数组排序而已。
由此我们可以想到,可以将“寻找逆序对”的过程类比成排序里的“比较”过程,因此寻找更优的排序方式可以降低比较的次数并时间复杂度。
这里我们可以运用分治思想,其中的典型就是归并排序。
归并排序
(图片来源:https://zhuanlan.zhihu.com/p/124356219)
因为子数组已经是有序的了,我们只需要在两个子数组合并成一个大数组的时候,看看位于后面的数组中的元素在被合并时,前一个数组还有多少个元素没有插入,就知道前一个数组中有多少数比它大了。(递归到最底层只有2个数的情况也是如此)
归并求逆序数

最后我们只需要把每一次求出来的个数相加就能求得逆序对的个数。

AC代码

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include <malloc.h>
long long int count=0;//int不够存
void merge(int x[ ],int tmp[ ],int left,int leftend,int rightend)
{     
       int i=left, j=leftend+1, q=left;
       while(i<=leftend && j<=rightend)
              if(x[i]<=x[j]) 
                    {tmp[q++]=x[i++];}
              else
                    {
						tmp[q++]=x[j++];
						count+=(leftend-i+1);//当插入后面的数组的元素的时候,看看前面的数组还有多少元素没有插入
					}
		while(i<=leftend)
	      tmp[q++]=x[i++];
	    while(j<=rightend)
           tmp[q++]=x[j++];
     	for(i=left; i<=rightend; i++) x[i]=tmp[i];
} 
void mSort(int k[], int tmp[], int left, int right)
{
    int center;
    if(left < right){
        center = (left+right)/2;
        mSort(k, tmp, left, center);
        mSort(k, tmp, center+1, right);
        merge(k, tmp, left,center, right);
    }
}
void mergeSort(int k[ ],int n)
{
    int *tmp;
    tmp = (int *)malloc(sizeof(int) * n);
    if(tmp != NULL) {
        mSort(k, tmp, 0, n-1);
        free(tmp);
    } 
    else
        printf("No space for tmp array!!!\n");
}//以上三段是经典的归并排序,求逆序对的时候仅仅加了一行代码而已
int main()
{
	int n,i,j;
	scanf("%d",&n);
	int *a=(int*)malloc(n*sizeof(int));
	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	mergeSort(a,n);
	printf("%lld",count);
	free(a);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值