题目
给定一个数组 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;
}
}
结果
关注个人微信公众号:肌肉码农,回复“好友”讨论问题