摘要:
归并排序模板题。
题目
给定一个数组 $nums$ ,如果 $i < j$ 且 $nums[i] > 2 \times nums[j]$ 我们就将 $(i, j)$ 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
示例 1:
输入: [1,3,2,3,1]
输出: 2
示例 2:
输入: [2,4,3,5,1]
输出: 3
注意:
- 给定数组的长度不会超过 $50000$。
- 输入数组中的所有数字都在 $32$ 位整数的表示范围内。
归并思想
这道题和AcWing归并排序模板题 【AcWing算法基础】第一讲-基础算法-归并排序 AcWing 788. 逆序对的数量 非常类似,都是利用区间的单调性对pair的比较进行优化。
递归地划分整个数组区间。从最小两个数(或一个数)开始「归」或者叫回溯,在回溯过程中对数组当前的两个区间进行排序,维护它们的单调性,为上一层做准备。同时,在这个过程中另行维护两个指针 i
, j
, 分别处理两个片段。判断条件nums[i] > 2*nums[j]
。如果当前nums[i] > 2*nums[j]
,由于此时 i
所在区间是单调递增的,所以从 i
到区间结束的地方所有的数都满足这个条件;反之,移动 i
。
1 | class Solution { |
- 因为题目给出数组长度不超过 $50000$,所以有 $49999!$ 个对数,显然结果最大超过
int
类型范围。- 像二叉树遍历一样递归地将原数组一分为二。
- 维护两个指针分别处理两个片段。
- 因为
2 * nums[j]
可能会超出int
类型最大可取值,所以需要强制转换成long long
。- 如果满足条件,那么由于片段内数组数字是单调递增的,所以从
i
起到片段结束都满足条件。接着移动j
,因为j++
后对应的数组数字增加,所以需要从i
起和j
重新比较。- 不满足条件的话,移动
i
,看增加之后是否满足条件。- 将两个片段作为一个整体排序,这个整体将作为回溯到上一层的一个片段。
注意这道题和 【AcWing算法基础】第一讲-基础算法-归并排序 AcWing 788. 逆序对的数量 的不同之处在于,后者可以在归并排序的两两比较的同时进行逆序对的计数,绝对不会漏掉。
但是这道题因为比较条件不同,如果在归并排序的同时进行计数,会漏掉一些数据。
比如数组排序到最后一层:
$1, 2, 3, 1, 3$
left: $1, 2, 3$, right: $1, 3$
会漏掉 $(3, 1)$ 这一对。
树状数组
等我学会之后再来完成。
原题链接: LeetCode 493. 翻转对