题目:
给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
// 归并排序 = 分治 + 归并
// 分治常用 partition 或者是 split
/*
本题就是用常规的归并排序的套路来完成的
只不过需要再归并之前,对 左右两个部分的数组计算一下逆序对儿
1、分治,就是把数组分组,每次分成两部分,前面和后面,以 [1,3,2,3,1] 为例
注意分治的思想,每次都是一分为二,通过递归实现,当递归到最后一层,也就是递归到只剩下一个元素时才开始计算,并回溯
分治后开始 实现归并,也就是在回溯的过程中开始实现归并
2、归并
第一轮:左:nums[0] 右:nums[1]
第二轮:左:nums[0],nums[1] 右:nums[2]
当第二轮结束,说明左边的归并也完成了,此时左边已经是有序的数组
第三轮:左:nums[3] 右:nums[4]
第三轮结束,说明右边的归并完成了,此时右边也是有序数组了
第四轮:左:nums[0],nums[1],nums[2] 右:nums[3],nums[4]
第四轮就是需要把左右两个有序的数组合并为一个有序数组,整个归并排序的过程就完成了
3、在归并之前计算逆序对儿
因为传到merge函数的左右两个子数组已经是有序的了,此时要做的工作就是
for循环遍历左边的子数组,
在for里面用while边遍历边判断是否存在逆序对
因为两个子数组的顺序已经各自排好了,所以只需要判断对于左边的每一个nums[i]是否在右边存在比nums[i]小的数
如果存在,则逆序对数量加1
注意是在归并之前计算逆序对。
*/
class Solution {
public:
int reversePairs(vector<int>& nums)
{
return partition(nums, 0, nums.size() - 1);
}
private:
// 第一个辅助函数 分治
// 输入参数:数组、左边界、右边界 返回参数:逆序对的个数
int partition (vector<int>& nums, int left, int right)
{
if(left >= right) return 0;
int mid = (left + right) / 2; // Q1:为什么不用 右移呢?右移操作很快呀
int res = 0; // 可以把 res 定义为一个 成员变量,或者是作为传入的形参
// 再分为左右两个区间,递归实现,递归的深度是 O(logn)
res += partition(nums, left, mid); // 先对左区间进行递归分解
res += partition(nums, mid + 1, right); // 再对右区间进行递归分解
// 递归完成后,进行 归并
res += merge(nums, left, mid, right);
return res;
}
// 第二个辅助函数 归并
// 输入参数:数组、左边界、中间下标、右边界 返回参数:逆序对的个数
int merge (vector<int>& nums, int left, int mid, int right)
{
// 归并之前先计算逆序对,然后再进行正常的归并
int temp = mid + 1;
int res = 0;
// 在这里计算 逆序对
for(int i = left; i <= mid; ++i)
{
// 2l 强制类型转换为 long 防止溢出,能用乘就不用除法,除法会向下取整,导致数据的大小关系发生改变
while (temp <= right && 2l * nums[temp] < nums[i]) temp++;
res += temp - mid - 1;
}
// 注意这里在定义的时候直接分配好空间会更好: 预分配所有内存比在回推期间调整内存大小更好
vector<int> tmp(right - left + 1);
int start = left, end = mid + 1, index = 0;
while(start <= mid && end <= right)
{
if(nums[start] < nums[end]) tmp[index++] = nums[start++];
else tmp[index++] = nums[end++];
}
while(start <= mid) tmp[index++] = nums[start++];
while(end <= right) tmp[index++] = nums[end++];
for(int j = left; j <= right; ++j) nums[j] = tmp[j - left];
return res;
}
};