leetcode 493 翻转对

题目描述

给定一个数组nums,若nums[i] > 2 * nums[j],则称(i,j)为一个翻转对,求出数组中翻转对的数量。

思路

本题若直接使用双重循环,时间复杂度为0(N2)。测试用例会超时。所以需要将时间复杂度降到0(nlogn)。该题可考虑分治法。

假设数组A,若将A分成A1和A2,则A中翻转对的数量等于下标i和j均在A1中的翻转对数量加上下标i和j均在A2中的翻转对数量以及i和j分别在A1和A2中的数量。即:

s o l v e ( A ) = s o l v e ( A < s u b > 1 < / s u b > ) + s o l v e ( A < s u b > 2 < / s u b > ) + c r o s s m i d d l e ( A < s u b > 1 < / s u b > , A < s u b > 2 < / s u b > ) solve(A) = solve(A<sub>1</sub>) + solve(A<sub>2</sub>) + crossmiddle(A<sub>1</sub>,A<sub>2</sub>) solve(A)=solve(A<sub>1</sub>)+solve(A<sub>2</sub>)+crossmiddle(A<sub>1</sub>,A<sub>2</sub>)

所以我们可以通过分治法将问题分解为子问题,求解后再合并。这和归并排序的思想有所类似,而我们同时也可以通过归并排序对求解子问题的方法进行简化。

对于两个有序数组A1和A2,求解crossmiddle情况下的翻转对可以减少不必要的操作。对A2遍历,对每一个j,在A1中遍历,寻找满足nums[i] > 2 * nums[j]的i,若找到了,则i之后一直到mid的数都满足条件,因为是有序数组,i后的数字都要比nums[i]大。

这使得很多冗余操作被舍去。所以我们只需在归并排序中合并两个有序数组之前执行该操作,并将子问题结果合并即可。在这之后继续归并排序的操作,又一次得到两个有序数组,继续上述操作。直到数组有序,原问题的解也被合并了出来。

c++代码如下:

class Solution {
public:
    int find_pairs(vector<int>& nums,int& left,int& right){
        int mid = (left + right)/2;
        int i = left,j = mid + 1;
        int res = 0;
        while(i <= mid){
            while(j <= right && (long)nums[i] > 2 * (long)nums[j]){
                res += mid - i + 1;
                j++;
            }
            i++;
        }
        return res;
    }
    int merge_sort(vector<int>& nums,int nums_sort[],int left,int right){
        if(left >= right) return 0;
        int mid = (left + right) / 2;
        int res = merge_sort(nums,nums_sort,left,mid) + merge_sort(nums,nums_sort,mid + 1,right) + find_pairs(nums,left,right);//求解当前数组的翻转对
        int i = left,j = mid + 1,index = left; 
        while(i <= mid && j <= right){
            if (nums[i] <= nums[j]) nums_sort[index++] = nums[i++];
            else nums_sort[index++] = nums[j++];
        }
        while(i <= mid) nums_sort[index++] = nums[i++];
        while(j <= right) nums_sort[index++] = nums[j++];
        for (int index = left;index <= right;index++) nums[index] = nums_sort[index];
        return res;//将结果返回至上一层 
    }
    int reversePairs(vector<int>& nums) {
        if (nums.empty()) return 0;
        int left = 0,right = nums.size() - 1;
        int nums_sort[nums.size()];
        memset(nums_sort,0,sizeof(nums_sort));//数组初始化为0
        return merge_sort(nums,nums_sort,left,right);
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值