题目描述
给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
解题思路
算法
归并算法,合并时进行翻转对的统计
思路
利用归并排序在合并时左,右区间都是升序排列的特点进行翻转对统计,这样既方便而且不会重复,因为在合并之后两个区间会作为整体来和其他区间进行比较
过程
在合并过程中,令左区间的指针为 p1, 右为 p2,根据合并排序有 l <= p1 <= mid, mid + 1 <= p2 <= r,其中 l 为左边界,r 为右边界,mid 是划分左区间和右区间的边界
其中:
左区间为 [l, mid]
右区间为 [mid + 1, r]
在合并之前,通过 while 循环统计翻转对个数
统计过程中,检查每个 nums[p1] 是否和当前的 nums[p2] 符合翻转对的要求,如果不符合 p1++,否则做一次统计之后 p2++
具体过程是:
通过递归分别统计划分的左区间和右区间,并记录到统计结果 ans 上
当左区间遍历到 nums[p1],右区间合并到 nums[p2] 时,如果此时符合翻转对的要求,即 nums[p1] > 2 * nums[p2],因为左区间是递增的,所以 p1 之后的所有左区间元素也都符合条件,因此我们要把统计结果 ans 加上从 p1 到左区间右边界 mid 的长度,即 mid - p1 + 1
记录后,继续检查下一个右区间元素,让 p2 递增
如果当前遍历到的 nums[p1] 不符合要求,直接递增 p1
特别注意
与统计逆序对不同,我们不能在合并 p2 元素的时候才统计翻转对,因为符合要求的左区间元素很有可能已经被合并,这是因为:
合并元素之要求比大小,而翻转对则要求比较相差的倍数
代码
/**
* @param {number[]} nums
* @return {number}
*/
var reversePairs = function (nums) {
return countResult(nums, 0, nums.length - 1)
}
function countResult(nums, l, r) {
// if the section length <= 1, reverse pairs number is 0
if (l >= r) return 0
let ans = 0
const mid = (l + r) >> 1,
temp = []
// pairs in the left section
ans += countResult(nums, l, mid)
// pairs in the right section
ans += countResult(nums, mid + 1, r)
// pairs across the left & right
let k = 0,
p1 = l,
p2 = mid + 1
while (p1 <= mid && p2 <= r) {
if (nums[p1] > 2 * nums[p2]) {
ans += mid - p1 + 1
p2++
} else {
p1++
}
}
p1 = l
p2 = mid + 1
while (p1 <= mid || p2 <= r) {
if (p2 > r || (p1 <= mid && nums[p1] < nums[p2])) {
temp[k++] = nums[p1++]
} else {
temp[k++] = nums[p2++]
}
}
for (let i = l; i <= r; i++) {
nums[i] = temp[i - l]
}
return ans
}