求序列的逆序数

逆序数概念:

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

即,在数组中,若0=<i<j<=n-1,有a[i]>a[j],就说明这两个数是逆序对,一个数组中逆序对数量就是逆序数

代码实现思路:

第一种思路:用两个for循环解决,用第一个循环i从数组开头开始,另一个循环j从i+1开始,即此时满足了,i<j,比较a[i]和a[j]的大小,若a[i]>a[j],那就逆序数+1.

这种方法的是时间复杂度是O(n^2),n为数组的长度。若当n=100000的时候,就会超时。

所以这种方法的缺陷就是时间复杂度过大,下面提出另一种思路。

第二种思路:参考归并排序的思想(具体归并排序可参考我另一篇博客: 归并排序原理讲解及C++实现

1)将数组分成两部分,分别求出左半边逆序数和右半边的逆序数

2)再计算有多少逆序是由左半边取一个数和右半边取一个数构成的

其中,第二小步的关键:左半边和右半边要排好序(因为是找前面比后面大的,所以一般从大到小排)。

这种方法的时间复杂度是O(nlogn),比第一种方法的时间复杂度要低。

综上所述,所以一般使用第二种,归并排序的思想来做,防止超时。

逆序数算法实现代码(C++):

#include<iostream>
#include<cstdio>
using namespace std;
int a[100010];
int b[100010];

void Merge(int a[],int s,int m,int e,int b[])
{
	int pb = 0;
	int p1 = s,p2 = m+1;
	while(p1<=m && p2<=e)
	{
		if(a[p1] > a[p2])
			b[pb++] = a[p1++];
		else
			b[pb++] = a[p2++];
	}
	while(p1<=m)
		b[pb++] = a[p1++];
	while(p2<=e)
		b[pb++] = a[p2++];
	
	for(int i = 0;i < e-s+1;i++)
		a[s+i] = b[i];
	
}

long long Count(int a[],int s,int m,int e)
{
	long long res = 0;
	int p1 = s;
	int p2 = m+1;
	while(p1<=m && p2<=e)
	{
		if(a[p1] > a[p2])
		{
			res += (e-p2+1); 
			p1++;
		}
		else
			p2++;
	}
	
	return res;
	
}

long long MergeSort_Res(int a[],int s,int e,int b[])
{
	long long res = 0;
	if(s < e)
	{
		int m = s + (e-s)/2;
		res += MergeSort_Res(a,s,m,b);
		res += MergeSort_Res(a,m+1,e,b);
		
		res += Count(a,s,m,e);
		Merge(a,s,m,e,b);
	}
	return res;
}


int main()
{
	int n;
	scanf("%d",&n);
	for(int i = 0;i < n;i++)
		scanf("%d",&a[i]);
	
	printf("%lld\n",MergeSort_Res(a,0,n-1,b));
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mikchy/article/details/81221564
今日推荐