#include <algorithm>
#include <random>
#include <vector>
class Solution {
public:
long int countMerge(vector<int>& data,vector<int>& assist,int start,int end,int mid)
{
//如果当前只有一个元素,逆序数记为0(递归出口)
if(start>=end)
{
return 0;
}
else
{
//递归
int midl=start+(mid-start)/2;//左边区域的逆序数
long int leftcount=countMerge(data,assist,start,mid,midl);
int midr=mid+1+(end-mid-1)/2;//右边区域的逆序数
long int rightcount=countMerge(data,assist,mid+1,end,midr);
//计算两个区域合并在一起的逆序数
int i=start,j=mid+1;
int tempcount=0;
while(i<=mid)
{
if(data[j]<data[i]&&j<=end)
{
++tempcount;
++j;
continue;
}
leftcount+=tempcount;
++i;
}
//归并
i=start,j=mid+1;
int start2=start;
while(i<=mid&&j<=end)
{
if(data[i]<data[j])
{
assist[start2++]=data[i];
++i;
}
else
{
assist[start2++]=data[j];
++j;
}
}
while(j<=end)
{
assist[start2++]=data[j];
++j;
}
while(i<=mid)
{
assist[start2++]=data[i];
++i;
}
while(start<start2)//注意这里是小于,start2多向后移动了一位
{
data[start]=assist[start];
++start;
}
//返回逆序数
return leftcount+rightcount;
}
}
int InversePairs(vector<int> data)
{
vector<int> assist;
assist.resize(data.size()+1);//注意一定要resize()否则会溢出错误
long int res=countMerge(data,assist,0,data.size()-1,(data.size()-1)/2);
return res%1000000007;
}
};
给定数组data,如果从中间分为左右两部分,如果左边逆序数为n, 右边逆序数为m,那么总逆序数为n+m+x;x为左边元素与右边元素组成逆序对的逆序数(一个序列总可以经过多次划分,分为左边或者右边元素个数为1,此时n/m为0,因此只要能计算x,就能设计递归解决这个题)。
不难发现,x的值与左右两边内部排序没有关系。如果左右两边都是升序列,那么可以在O(n)时间范围内求出x(这是因为计算左边每个元素逆序数时,右边不用回溯)。
根据以上描述,此题实际上是归并排序,只不过多加了一个计算x,归并有序序列的时间复杂度也是O(n),因此总复杂仍为O(nlogn)。



京公网安备 11010502036488号