一、题目
1、题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例1:
输入: [7,5,6,4]
输出: 5
2、基础框架
class Solution {
public:
int reversePairs(vector<int>& nums) {
}
};
3、原题链接
剑指 Offer 51. 数组中的逆序对
二、解题报告
1、思路分析
(1)利用 归并排序 在合并时数组部分有序的特征,合并时分为左组和右组,同组之间不考虑逆序对,因为在上一层合并的过程中已经考虑过了
(2)小技巧:合并时的左组和右组,从右往左合并,当右组的当前元素已经小于左组当前元素时,那么对于左组当前元素,逆序对的个数就是 p2 - mid + 1 - 1 = p2 - mid
2、时间复杂度
O ( n l o g n ) O(nlogn) O(nlogn)
3、代码详解
class Solution {
private:
int merge(vector<int> &arr, int l, int mid, int r) {
vector<int> help(r - l + 1);
int p1 = mid;
int p2 = r;
int ind = r - l;
int cnt = 0;
//合并时从右往左合并,能快速定位到比当前左组元素小的右组元素个数
while (p1 >= l && p2 > mid) {
cnt += arr[p1] > arr[p2] ? p2 - mid : 0;
help[ind--] = arr[p1] > arr[p2] ? arr[p1--] : arr[p2--];
}
while (p1 >= l) {
help[ind--] = arr[p1--];
}
while (p2 > mid) {
help[ind--] = arr[p2--];
}
for (int i = 0; i < help.size(); i++) {
arr[l + i] = help[i];
}
return cnt;
}
int process(vector<int> &nums, int l, int r) {
if (l == r) return 0;
int mid = l + ((r - l) >> 1);
int left = process(nums, l, mid);
int right = process(nums, mid + 1, r);
int mergeAns = merge(nums, l, mid, r);
return left + right + mergeAns;
}
public:
//目标:求每个数字右边比它小的数字个数
int reversePairs(vector<int>& nums) {
if (nums.size() < 2) return 0;
return process(nums, 0, nums.size() - 1);
}
};