leetcode 493.翻转对
题干
给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
示例 1:
输入: [1,3,2,3,1]
输出: 2
示例 2:
输入: [2,4,3,5,1]
输出: 3
注意:
给定数组的长度不会超过50000。
输入数组中的所有数字都在32位整数的表示范围内。
题解
用归并排序的思路非常清晰:
1.对于两个已有序的数组来说,假设答案已经包含了两个数组内部存在的重要翻转对的数量,那么我们现在需要做的就是求两个元素分别位于两个数组内时新增加的重要翻转反对。
2.因此我们只要维护两个指针,对于每个确定的左数组的数,我们遍历右数组的数,如果符合条件则重要翻转对加一(因为此时还没有排序,右数组的下标在原数组中一定大于左数组)
3.同时可以注意到,由于左数组已经是有序的,所以右数组遍历的起点也应该随着左数组不断右移,这里可以设为对于上一个左数组的数,右数组中第一个符合条件的数的下标。
4.这道题还有个坑,由于数据最大可到32位整数,所以当2 * 数据有可能溢出,所以在判断的时候要将比较的两个元素强制转换为long long
你以为我写了这么多话就能过了?其实我这么写会超时
超时的代码:
class Solution {
public:
int ans = 0;
//合并两个子数组,并统计统计重要翻转对数量
void merge(int l,int r,int m,vector<int>& nums,int& ans){
//先统计两个子数组能新组成的重要翻转对的数量,即对于左数组中每个值,遍历右数组,找到两倍仍小于左数组元素的即为一个重要翻转对
//两个子数组的下标范围分别为l - m , m+1 - r
int leftPtr = l;
int RightPtr = m + 1;
while(leftPtr <= m){
int tempRightPtr = RightPtr;
bool firstFlag = true;
while(tempRightPtr <= r){
if((long long)nums[leftPtr] > 2 * (long long)nums[tempRightPtr]){
ans++;
if(firstFlag){
RightPtr = tempRightPtr;
firstFlag = false;
}
}
tempRightPtr++;
}
leftPtr++;
}
//再合并两个子数组
vector<int> temp;
leftPtr = l;
RightPtr = m + 1;
while(leftPtr <= m || RightPtr <= r){
if(nums[leftPtr] < nums[RightPtr]){
temp.push_back(nums[leftPtr]);
leftPtr++;
}else{
temp.push_back(nums[RightPtr]);
RightPtr++;
}
if(leftPtr > m){
while(RightPtr <= r){
temp.push_back(nums[RightPtr]);
RightPtr++;
}
}else if(RightPtr > r){
while(leftPtr <= m){
temp.push_back(nums[leftPtr]);
leftPtr++;
}
}
}
copy(temp.begin(),temp.end(),nums.begin() + l);
return;
}
//归并排序递归实现
void mergeSort(int l,int r,vector<int>& nums,int& ans){
if(l == r) return;
int m = (l + r) / 2;
mergeSort(l,m,nums,ans);
mergeSort(m+1,r,nums,ans);
merge(l,r,m,nums,ans);
return;
}
int reversePairs(vector<int>& nums) {
int n = nums.size();
if(n==0 || n==1) return 0;
mergeSort(0,n-1,nums,ans);
return ans;
}
};
其实只要稍微改一下统计重要翻转对的部分就行了
因为两个子数组都是已经有序的,所以对于一个更大的左数组中的元素,他所组成的最大翻转对的右元素一定包含比他小的左元素所形成的的最大反转对的右元素:
...
while(leftPtr <= m){
while(RightPtr <= r && (long long)nums[leftPtr] > 2 * (long long)nums[RightPtr]){
RightPtr++;
}
ans += RightPtr - m - 1;
leftPtr++;
}
...
(虽然效率并不高)