設\(A\)為一個有\(n\)個數字的序列,其中所有的數字各不相同。如果存在正整數\(i\)和\(j\),使得\(1 \le i \lt j \le n\)且\(A[i] \gt A[j]\),那么數對\((A[i], A[j])\)就被稱為\(A\)的一個逆序對,也稱作逆序,逆序對的數量就是逆序數。如下圖所示,\((A[2], A[4])\)就是一個逆序對。

分治法
假設我們要統計數列\(A\)中逆序對的個數。如圖所示,我們可以將數列\(A\)分成兩半得到數列B和數列C。因此,數列A中所有的逆序對必屬於下面三者其一:
(1). \(i,j\)都屬於數列\(B\)的逆序對
(2). \(i,j\)都屬於數列\(C\)的逆序對
(3). \(i\)屬於數列\(B\)而\(j\)屬於數列\(C\)的逆序對
所以,我們只需要分別統計這三種逆序對,然后再加起來就行了。(1)和(2)可以通過遞歸求得,對於(3),我們可以對數列\(C\)中的每個數字,統計在數列\(B\)中比它大的數字的個數,再把結果加起來即可。因為每次分治時數列的長度都會減半,所以遞歸的深度是\(O(\log n)\),而每一層有\(O(n)\)個操作,因此算法的時間復雜度為\(O(n\log n)\)。
class Solution {
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
if (n <= 1) return 0;
auto mid = nums.begin() + n / 2;
vector<int> left(nums.begin(), mid);
vector<int> right(mid, nums.end());
int cnt = 0;
cnt += reversePairs(left);
cnt += reversePairs(right);
int nums_idx = 0;
int left_idx = 0;
int right_idx = 0;
while (nums_idx < n) {
if (left_idx < left.size() && (right_idx == right.size() || left[left_idx] <= right[right_idx])) {
nums[nums_idx++] = left[left_idx++];
} else {
cnt += n / 2 - left_idx;
nums[nums_idx++] = right[right_idx++];
}
}
return cnt;
}
};
樹狀數組
我們構建一個值的范圍是\(1\sim n\)的樹狀數組,按照\(j=0,1,2,\cdots,n-1\)進行如下操作:
- \(j-\text{sum}(A[j])\)
- \(\text{add}(A[j], 1)\)
對於每個\(j\),樹狀數組查詢得到的前\(A[j]\)項的和就是滿足\(i \lt j, A[i] \le A[j]\)的\(i\)的個數。因此,把這個值從\(j\)中減去,得到的就是滿足\(i \lt j, A[i] \gt A[j]\)的\(i\)的個數。由於對每個\(j\)的復雜度是\(O(\log n)\),所以整個算法的復雜度是\(O(n\log n)\)。注意到樹狀數組的范圍是\(1\sim n\),而原始序列中可能包含負數或者非常大的數,因此我們需要對原始數據進行一次映射(離散化),把原始數據映射到\(1\sim n\)區間上。
class Solution {
public:
int reversePairs(vector<int>& nums) {
if (nums.size() <= 1) return 0;
vector<int> nums_copy(nums.begin(), nums.end());
sort(nums_copy.begin(), nums_copy.end());
unordered_map<int, int> num_val_map;
n = 1;
// 離散化
for_each (nums_copy.begin(), nums_copy.end(), [&num_val_map, this](const int val) {
if (num_val_map[val] == 0) num_val_map[val] = n++;
});
bits.resize(n);
fill(bits.begin(), bits.end(), 0);
int ans = 0;
for(int i = 0;i < nums.size(); ++i) {
ans += i - sum(num_val_map[nums[i]]);
add(num_val_map[nums[i]], 1);
}
return ans;
}
private:
int n;
vector<int> bits;
inline int lowbit (int x) {
return x & (-x);
}
void add (int idx, int val) {
while (idx < n) {
bits[idx] += val;
idx += lowbit(idx);
}
}
int sum (int idx) {
int res = 0;
while (idx > 0) {
res += bits[idx];
idx -= lowbit(idx);
}
return res;
}
};
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
vector<Pair> num_pairs(nums.size());
vector<int> counts(nums.size(), 0);
for (int i = 0;i < nums.size();++i) {
num_pairs[i].first = nums[i];
num_pairs[i].second = i;
}
reversePairs(num_pairs, counts);
return counts;
}
private:
using Pair = pair<int, int>;
void reversePairs(vector<Pair>& nums, vector<int>& counts) {
int n = nums.size();
if (n <= 1) return;
auto mid = nums.begin() + n / 2;
vector<Pair> left(nums.begin(), mid);
vector<Pair> right(mid, nums.end());
reversePairs(left, counts);
reversePairs(right, counts);
int nums_idx = 0;
int left_idx = 0;
int right_idx = 0;
while (nums_idx < n) {
if (left_idx < left.size() && (right_idx == right.size() || left[left_idx].first <= right[right_idx].first)) {
nums[nums_idx++] = left[left_idx++];
} else {
for (int i = left_idx; i < n/2; i++) {
counts[left[i].second] += 1;
};
nums[nums_idx++] = right[right_idx++];
}
}
}
};