leetcode-剑指 Offer 51. 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
解题思路:
首先想到的是暴力的方式,遍历数组,然后再从每一个数字后一位开始遍历,查找所有比他小的数字,计数++。但是0 <= 数组长度 <= 50000,时间复杂度为O(n*n),会超时,所以此方法不可行。
然后想到了二分降低时间复杂度,但是这道题提并不适合,然后联想到了分治,但是实现代码就有点困难啦 。。。。。
向高人请教,据说本题就是归并排序(小白目前还是不太明白归并代码),只不过引入了一个ans记录逆序对的个数。
分治算法将原问题分解成小规模的问题,然后根据子问题的结果构造出原问题的答案。有点类似动态规划,所以运用分治算法也需要满足一些条件:原问题的答案应该可以通过子问题结果计算。典型的分治算法就是归并排序,核心逻辑如下:
void Sort(vector<int>&nums,int left,int right)
{
if(left>=right) return ;
int mid=left+(right-left)/2;
Sort(nums,left,mid); //分
Sort(nums,mid+1,right);
Merge(nums,left,mid,right); //治
}
本题代码:
class Solution {
int ans=0;
void Sort(vector<int>&nums,int left,int right) //‘分’着处理每一个区间
{
if(left>=right) return ;
int mid=left+(right-left)/2;
Sort(nums,left,mid);
Sort(nums,mid+1,right);
Merge(nums,left,mid,right);
}
void Merge(vector<int> &nums,int left,int mid,int right) //将区间合并
{
int tmp[nums.size()];
int i=left,j=mid+1,k=left;
while(i<=mid&&j<=right)
{
if(nums[i]<=nums[j])
tmp[k++]=nums[i++];
else //前面的元素大于后面的 才满足逆序对,需要计数
{
tmp[k++]=nums[j++];
ans+=mid-i+1;
}
}
while(i<=mid) tmp[k++]=nums[i++];
while(j<=right) tmp[k++]=nums[j++];
for(int i=left;i<=right;i++) //合并的区间就是从left-right,所以拷贝的也是该区间
nums[i]=tmp[i];
}
public:
int reversePairs(vector<int>& nums) {
ans=0;
Sort(nums,0,nums.size()-1);
return ans;
}
};