题目
思路一 归并排序
分治法,把数组中元素不断分成一半排序,再把排序后的结果合在一起。合在一起时,用双指针,如果右边的数比左边的小就增加相应逆序对的个数。
代码一
class Solution {
public:
// vector<int> tmp;
int reversePairs(vector<int>& nums) {
int n=nums.size();
// tmp.resize(n);
vector<int> tmp(n);
return n==0?0:mergeSort(nums,tmp,0,n-1);
}
int mergeSort(vector<int>& nums,vector<int>& tmp,int l,int r){
if(l>=r) return 0;
int mid=l+r>>1;
int res=mergeSort(nums,tmp,l,mid)+mergeSort(nums,tmp,mid+1,r);
// vector<int> tmp(nums.size());
// tmp=nums;
//下面这种写法只需要复制当前需要归并的,上面那种是全部复制,会超时
for(int k=l;k<=r;k++)
tmp[k]=nums[k];
int i=l,j=mid+1,k=l;
while(k<=r){
if(i==mid+1)
nums[k++]=tmp[j++];
else if(j==r+1 || tmp[i]<=tmp[j])
nums[k++]=tmp[i++];
else{
nums[k++]=tmp[j++];
res+=mid-i+1;
}
}
return res;
}
};
思路二 树状数组
从后往前遍历数组,把当前元素放入树状数组中,并累加当前树状数组中小于当前数的数的个数。但由于原数组的范围可能非常大,直接把元素值放入树状数组中会导致数组非常稀疏。因此,需要实现一个优化,先把原数组复制一份并排序,再把数据的相对位置(即排序后的排名)记录下来,树状数组要以1开始,所以要加1。这里用到了lower_bound函数,目的是在升序排序的数组中找到第一个大于等于num的指针。upper_bound是在升序排序的数组中找到第一个大于num的指针。
代码二
class BIT{
int n;
vector<int> tree;
public:
BIT(int _n):n(_n),tree(_n+1){};
static int lowbit(int x){
return x&(-x);
}
//求树状数组中小于等于x的所有数之和
int getSum(int x){
int sum=0;
while(x){
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
//每添加一个数,树状数组中所有由这个数组成的元素都要++
void update(int x){
for(;x<=n;x+=lowbit(x))
++tree[x];
}
};
class Solution {
public:
int reversePairs(vector<int>& nums) {
int n=nums.size(),ans=0;
//离散化
vector<int> tmp=nums;
sort(tmp.begin(),tmp.end());
for(int& num:nums){
num=lower_bound(tmp.begin(),tmp.end(),num)-tmp.begin()+1;
}
//树状数组统计逆序对
BIT bit(n);
for(int i=n-1;i>=0;i--){
ans+=bit.getSum(nums[i]-1); //要严格小于,不统计当前这个数,所以要-1
bit.update(nums[i]);
}
return ans;
}
};