我们可以利用归并排序在合并两个数组时会比较两个数组中的值来确定有多少逆序对。我们用左指针指向左边的数组,右指针指向右边的数组。每当左指针右移时,我们在总数上加上右指针指向位置与起始位置的差值即可。
class Solution {
public:
int mergeSort(vector<int>& nums, vector<int>& tmp, int l, int r) {
if (l >= r) {
return 0;
}
int mid = (l + r) / 2;
int inv_count = mergeSort(nums, tmp, l, mid) + mergeSort(nums, tmp, mid + 1, r);
int i = l, j = mid + 1, pos = l;
while (i <= mid && j <= r) {
if (nums[i] <= nums[j]) {
tmp[pos] = nums[i];
++i;
inv_count += (j - (mid + 1));
}
else {
tmp[pos] = nums[j];
++j;
}
++pos;
}
for (int k = i; k <= mid; ++k) {
tmp[pos++] = nums[k];
inv_count += (j - (mid + 1));
}
for (int k = j; k <= r; ++k) {
tmp[pos++] = nums[k];
}
copy(tmp.begin() + l, tmp.begin() + r + 1, nums.begin() + l);
return inv_count;
}
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int> tmp(n);
return mergeSort(nums, tmp, 0, n - 1);
}
};
我们可以是用树状数组来维护序列前缀和,利用 u p d a t e ( i , v ) update(i, v) update(i,v)来维护把序列 i i i位置的数加上一个值 v v v;利用 q u e r y ( i ) query(i) query(i)来维护区间 [ 1 , i ] [1,i] [1,i]的前缀和。我们可以根据数组中值的值域建立一个新的数组,其对应的位置上的值代表了每个值出现的次数,则区间 [ 1 , i − 1 ] [1,i-1] [1,i−1]的前缀和代表了数组中小于当前值的个数。因此我们可以从后向前遍历数组,记当前遍历的元素为 a i a_i ai,而后将对应的值加一,将 i − 1 i-1 i−1位置的前缀和加入答案中。
同时我们可以使用离散化进一步优化数组的空间,在维持数组大小相对不变的前提下进行去重。
class BIT {
private:
vector<int> tree;
int n;
public:
BIT(int _n): n(_n), tree(_n + 1) {}
static int lowbit(int x) {
return x & (-x);
}
int query(int x) {
int ret = 0;
while (x) {
ret += tree[x];
x -= lowbit(x);
}
return ret;
}
void update(int x) {
while (x <= n) {
++tree[x];
x += lowbit(x);
}
}
};
class Solution {
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int> tmp = nums;
// 离散化
sort(tmp.begin(), tmp.end());
for (int& num: nums) {
num = lower_bound(tmp.begin(), tmp.end(), num) - tmp.begin() + 1;
// 第一个不小于num的数的位置
}
// 树状数组统计逆序对
BIT bit(n);
int ans = 0;
for (int i = n - 1; i >= 0; --i) {
ans += bit.query(nums[i] - 1);
bit.update(nums[i]);
}
return ans;
}
};