493. Reverse Pairs


Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j].

You need to return the number of important reverse pairs in the given array.

Example1:

Input: [1,3,2,3,1]
Output: 2
Example2:

Input: [2,4,3,5,1]
Output: 3

Note:

  1. The length of the given array will not exceed 50,000.
  2. All the numbers in the input array are in the range of 32-bit integer.

方法1: merge sort

grandyang:https://www.cnblogs.com/grandyang/p/6657956.html

思路:

参见327. Count of Range Sum。同为妙用merge sort。

利用递归的思想,首先统计两边各有多少reverse pairs,然后再统计两段连起来会增加多少。那么如何才能将第二步用小于O(n^2)的方法算出来呢?可以用双指针来遍历两边,对于每一个左边的数字nums[i], 我们找到右边和它reverse的那个最大值的右边一个,也就是第一个终于满足 nums[i] <= 2 * nums[j] 的值,那么如果我们知道前面的都是有序的,而且都比nums[j]小,那么可以一次性累加 j - mid这么多pair。而对于下一个i,在找属于它的 j 时只可能在更右边,没有必要回溯,这也是排序才能保证的。所以排序可以保证我们用O(N)的时间找出merge步骤中的reverse pair,因而得到O(nlogn)的算法。而比较的过程仅仅是和merge sort采用了不一样的不等式,可以用一个cache来完成,或者用内置的inplace_merger(begin() + left, begin() + mid + 1, begin() + right + 1)。

Complexity

Time complexity: O(n logn)
Space complexity: O(log n)

易错点:(无穷无尽)

  1. base case是什么:当区间内只剩下一个数字的时候,是不可能有reverse pair的,返回值是0。而这个时候对应的条件是right <= left。
  2. mid应该归谁:在二分法中mid的极端条件是和left重合的,发生在left + 1 = right时。一般情况下没毛病,但是这里我们要比较左右的pair,就必须正确划分mid此时的归属。只有当mid在left的范围内时,才能启动两个元素的base case comparison。
  3. 右半边遍历时要找什么:第一个违反了 nums[i] / 2.0 > nums[j]的j,那么满足条件的pair数量是j - (mid + 1),因为区间为[mid + 1, j)。而这样做就要求 j 的遍历范围 <= right而不是< right。
  4. 注意上面这个判断句必须是 / 2.0,否则overflow。
  5. 最后可以采用sort或者inplace_merge来sort,后者快一点。sort的指针是左闭右开,inplace_merge的两段区间划分也是左闭右开,[left, middle), [middle, right),所以mid 和 right 这两个参数使用时都应该 + 1。
class Solution {
public:
    int reversePairs(vector<int>& nums) {
        return pairHelper(nums, 0, nums.size() - 1);
    }
    
    int pairHelper(vector<int> & nums, int left, int right) {
        if (right - left <= 0) return 0;
        int mid = left + (right - left) / 2;
        int res = pairHelper(nums, left, mid) + pairHelper(nums, mid + 1, right);
        //vector<int> cache(right - left + 1, 0);
        for (int i = left, j = mid + 1; i <= mid; i++) {
            while (j <= right && nums[i] / 2.0 > nums[j]) j++;
            res += j - (mid + 1);
        }
         //sort(nums.begin() + left, nums.begin() + right + 1);
        inplace_merge(nums.begin() + left, nums.begin() + mid + 1, nums.begin() + right + 1);
        return res;
    }
};

方法2:BIT

discussion:https://www.cnblogs.com/grandyang/p/6657956.html

思路:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值