剑指 Offer 51. 数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
思路解析
假设给定数组为[8, 10, 15, 20, 70, 9, 25, 50, 60, 100]
,这个数组的前一半和后一半都是有序的,那么此时这个数组的逆序对的个数应该如何统计?
指针i
指向[8, 10, 15, 20, 70]
首元素,指针j
指向[9, 25, 50, 60, 100]
首元素,比较var(i)
和var(j)
的大小。(这里var(i)
和var(j)
指i
和j
指向元素的值)
- 若
var(i) < = var(j)
,则此时i
和j
指向元素不构成逆序对,否则构成逆序对(j++
); - 若
var(i) < = var(j)
,则代表后半段的数组中,有j
个元素小于var(i)
;(这里假设i
和j
均从0开始) - 由此可统计出前半段的数组中每个元素所构成的逆序对的个数,且后半段数组中不存在逆序对。
使用归并排序的思想,当元素分组至每组仅有一个元素时,代表该组已经有序,返回此有序数组中逆序对的个数为0,随后进行归并,并记录每次归并所产生的数组的逆序对的个数。
时间复杂度\(O(n \log n)\),空间复杂度\(O(n)\)。
代码实现
class Solution {
private:
vector<int> orderedArray; // From large to small
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int> tmp(n);
return mergeSort(nums, tmp, 0, n - 1);
}
int mergeSort(vector<int>& nums, vector<int>& tmp, int left, int right) {
if(left >= right)
return 0;
int mid = (left + right) / 2;
int prev_count = mergeSort(nums, tmp, left, mid) + mergeSort(nums, tmp, mid + 1, right);
// Merge
int i = left;
int j = mid + 1;
int curr_count = 0;
int tmp_index = left;
while(i <= mid && j <= right) {
if(nums[i] <= nums[j]) {
tmp[tmp_index] = nums[i++];
curr_count += (j - mid - 1);
}
else{
tmp[tmp_index] = nums[j++];
}
tmp_index++;
}
while(i <= mid){
tmp[tmp_index++] = nums[i++];
curr_count += (j - mid - 1);
}
while(j <= right)
tmp[tmp_index++] = nums[j++];
for(tmp_index = left; tmp_index <= right; tmp_index++)
nums[tmp_index] = tmp[tmp_index];
return prev_count + curr_count;
}
};