LeetCode每日一题- 翻转对

题目

给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。

你需要返回给定数组中的重要翻转对的数量。

示例 1:

输入: [1,3,2,3,1]

输出: 2

示例 2:

输入: [2,4,3,5,1]

输出: 3

链接:https://leetcode-cn.com/problems/reverse-pairs

分析

这道题难度级别为“困难”,它的“困难”不在于解题思路,而是运行优化,解题思路为归并排序,如果忘记了归并排序思路需要先回忆一下。

大致思路为将数组递归二分,直到一个元素一组,判断相邻两组元素是否是重要翻转对,再将这个两元素进行排序,然后回到上一层两个元素一组,判断相邻两组元素是否重要翻转对,这个时候是两两对比了,然后依此类推。

文字不好描述,举例如下:

原数组:                                           1,2 ,6, 1

归并排序倒数第一层分组:               1|2     6|1    // 可以判断6,1这一组为重要翻转对

归并排序倒数第一层排序后结果:    1|2     1|6    

归并排序倒数第二层分组:              1,2   |   1,6     //组1,2与组1,6进行比较,没有重要翻转对

归并排序倒数第二层排序后结果:    1,1,2,6

完成上述逻辑代码挺快的,但提交时超时了,接下来就是优化了,优化思路如下:

数组: 1,2 ,6,7       1,1,2,3

如果不优化,完成这两组数组对比需要进行 4*4次

在6完成与1,1,2,3对比后,得到的结果是1,1,2能与6组成重要翻转对

那么7可以直接可以直接与3对比了,因为与3前面的元素一定是重要翻转对

代码

class Solution {
    public int reversePairs(int[] nums) {
       return mergeSort(0, nums.length-1, nums);
    }
    
    int mergeSort(int start, int end, int[] nums) {
        if(start >= end){
            return 0;
        }
        
        int mid = (end-start)/2+start;
        int count = mergeSort(start, mid, nums) + mergeSort(mid+1, end, nums);
        
        int[] tmp = new int[end-start+1];
        int tmpIndex = 0;
        int rightTmpIndex = mid+1;
       
        int rightIndex = mid+1;
        for(int i=start;i<=mid;i++){
             //性能优化 因为子数组是从小到大有序,如果上一个元素已经被判断是重要翻转对,那么这一个元素也一定是。
            count += rightIndex -(mid+1);
            while(rightIndex <= end && nums[i]/2.0 > nums[rightIndex]){
                count ++;
                rightIndex ++;
            }
            
            while(rightTmpIndex <=end && nums[rightTmpIndex] < nums[i]){
                tmp[tmpIndex++] = nums[rightTmpIndex++];
            }
            tmp[tmpIndex++] = nums[i];
        }
        
        while(rightTmpIndex<=end){
            tmp[tmpIndex++] = nums[rightTmpIndex++];
        }
        
        System.arraycopy(tmp, 0, nums, start, tmp.length);
        return count;
    }
}

结果

 关注个人微信公众号:肌肉码农,回复“好友”讨论问题

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值