LCR 170 | 交易逆序对的总数
题目:
在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数。
示例 1:
输入:record = [9, 7, 5, 4, 6]
输出:8
解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。
✨统计逆序对,可以理解为:对于数字x,记录它右方比x小的数字个数。将数组record中每个数字都求解一遍再相加,即使整个数组的逆序对个数。
✨我们用桶来记录数组record中,每个数字出现的次数。示例1得到的数组为:
index -> 1 2 3 4 5 6 7 8 9
value -> 0 0 0 1 1 1 1 0 1
可以看出,数组的前 i - 1 位的前缀和表示 比i小的数字个数。那么我们从后往前遍历record数组,对每个数字x,求它的前缀和c[i - 1]就是它右方比它小的数字个数。
但是,遍历时需要及时更新桶,就是将数字 x 对应 value + 1。
对于需要更新和查询前缀和的问题,可以用到树状数组!
✨关于桶的大小
record的数字是-1e9 ~ 1e9,但是我们不可能开一个这么大的数组。这时需要离散化树状数组。
我们先将原数组record去重排序,得到新数组a,对于每一次遍历,我们需要先得到该数组在数组a中的index,再通过新index执行更新、求和操作。
void Init(vector<int>& nums){
a.assign(nums.begin(), nums.end());
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
n = nums.size();
m = a.size();
c.resize(m + 1, 0);
}
🔑完整代码:
private:
int n, m;
vector <int> a,c;
void Init(vector<int>& nums){
a.assign(nums.begin(), nums.end());
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
n = nums.size();
m = a.size();
c.resize(m + 1, 0);
}
int getID(int x){
int l = 0, r = m, mid;
while(l <= r){
mid = (l + r) / 2;
if(a[mid] == x) return mid + 1;
else if(a[mid] > x) r = mid - 1;
else l = mid + 1;
}
return 0;
}
void upgrade(int index){
for(int i = index; i <= m; i += i & -i){
c[i]++;
}
}
int Sum(int index){
int res = 0;
for(int i = index; i > 0; i -= i & -i){
res += c[i];
}
return res;
}
public:
int reversePairs(vector<int>& record) {
Init(record);
int ans = 0, id;
for(int i = n - 1; i >= 0; i--){
id = getID(record[i]);
upgrade(id);
ans += Sum(id - 1);
// cout<<"i = "<<i<<" id = "<<id<<" Sum(id) = "<<Sum(id)<<endl;;
}
return ans;
}
};

浙公网安备 33010602011771号