解法1:树状数组+二分查找
基本思路
题目要找的是前面的数比后面的数的两倍大的数,那么我们可以从后向前遍历,将以前遍历过的数存起来,得到序列$sum$,然后在遍历过的数中找到比当前数的1/2倍小的数的个数,这样最终就可以得到解。
难点1:如何计算个数?
一开始想到的肯定是前缀和了,第$i$个前缀和表示小于等于第$i$个数的总个数,我们在序列$sum$中找到比当前数的1/2倍小的最大的数的下标$i$,然后求第$i$个前缀和就可以了。但是有个问题:前缀和的更新不方便。
这道题涉及到若干次查找和更新,所以我们可以考虑用树状数组来存遍历过的数的个数。所以我们定义树状数组$sum$来求前缀和,函数$getSum(i)$就可以求出第$i$个前缀和了。
难点2:如何找到比当前数的1/2倍小的最大的数的下标?
首先,我们将原序列从小到大排序得到序列$v$,然后用二分查找来找到比当前数的1/2倍小的最大的数的下标,且下标从1开始,这样做的原因是树状数组的下标是从1开始的,所以这样可以对应,然后求和。
难点3:如何更新树状数组?
首先更新树状数组需要知道第$i$个数是哪个数。由于第$i$个前缀和表示小于等于第$i$个数的个数,所以,我们需要用$map$来将拍好序的数字的位置确定下来,$map[j] = i$表示数字$j$是第$i$个数,这样,值小的数字的位置小,而且$map$中存的位置是这个数的最大位置,因为二分查找的时候找到的也是相同值的最大位置,这样可以对应。
代码
1 | class Solution { |
解法2:归并排序
基本思路
在归并排序中,我们先把序列分成小块,然后逐块合并排序,在本题中,相邻两块除了要排序以外,我们需要计算前一块中的数$nums[i]$和后一块中的$num[j]$可以形成多少个翻转对。
代码
1 | class Solution { |
