题目
给定一个数组 nums
,如果 i < j
且 nums[i] > 2*nums[j]
我们就将 (i, j)
称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
示例 1:
示例 2:
注意:
1. 给定数组的长度不会超过50000。
2. 输入数组中的所有数字都在32位整数的表示范围内。
题解
这道题目用树状数组加上hash来做。关于树状数组的介绍可以看树状数组百度百科 或者 树状数组
这里树状数组用来计数,表示当前索引对应的值出现的次数。由于数组元素是32
位整型,如果直接开一个数组包含nums
中所有元素的话(考虑到边界值),数组会很大,且效率很低,考虑题目中给出nums
大小不超过50000
,所以可以建立一个hash映射,将原始数组排序后映射为有序的索引值,由于题目中考虑的是nums[i] > 2*nums[j]
,所以原始数组中元素的2
倍的值也需要同时映射。
映射好了之后遍历原始数组,对每个元素的2
倍值,统计当前大于该值出现的总次数,即考虑树状数组中2*nums[i]
对应的索引值右侧元素出现的次数和。
代码
class Solution {
public:
int lowbit(int x){
return (int)x&(-1*x);
}
int getSum(int x,vector<int> &c){
int sum=0;
for(int i=x;i>0;i-=lowbit(i)){
sum+=c[i];
}
return sum;
}
void update(vector<int> &c,int x,int v){
for(int i=x;i<c.size();i+=lowbit(i)){
c[i]+=v;
}
}
int reversePairs(vector<int>& nums) {
int maxN=(INT_MAX)/2,minN=(INT_MIN)/2;
set<int> ss;
for(auto t:nums){
ss.insert(t);
if(t<=maxN&&t>=minN)
ss.insert(t*2);
}
unordered_map<int,int> m;
int n=ss.size();
auto it=ss.begin();
for(int i=1;i<=n;i++){
m[*it]=i;
it++;
}
vector<int> sum(n+1,0);
int res=0;
for(auto t:nums){
if(t<minN) res+=getSum(n,sum);
else if(t<maxN){
int idx=m[2*t];
res+=(getSum(n,sum)-getSum(idx,sum));
}
update(sum,m[t],1);
}
return res;
}
};