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;
    }
}

结果

LeetCode每日一题- 翻转对

© 版权声明

相关文章

暂无评论

none
暂无评论...