在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
记得以前是用树状数组写的,这次换个新解法----归并排序
利用归并排序特有的部分有序性质来解决逆序对问题
举例:
83691472
可以拆成
8369 1472
83 69 14 72
8 3 6 9 1 4 7 2
然后合并
38 69 14 27
3689 1247
12346789
这里就是归并排序的简单实现,不会的可以先去学下归并排序
可以假设前面是排好序的数组A,后面是排好序的数组B,利用指针i , j 指向A ,B
我们可以发现当 j指向的值先进队列时,因为逆序对是比自身小的后面的数
所以 j 指向的数可以与A数组中目前剩余的所有数都组成逆序对
可以思考一下,这样遍历完就是数组A+B的所有逆序对的总数。
所以完全可以在归并排序的自下而上的 归 过程中逐步得到所有的逆序对
具体代码:
class Solution {
public:
int judge(int l,int r,vector<int>& nums) //必须加&因为需要修改这个数组,保证分治的目的----有序
{
if(l==r) return 0;
int mid=l+(r-l)/2;
int left=judge(l,mid,nums); //分
int right=judge(mid+1,r,nums); //分
int cross=merge(l,r,mid,nums);
return left+right+cross;
}
int merge(int l,int r,int mid,vector<int>& nums)
{
int sum=0;
int i=l; // l - mid
int j=mid+1; // mid+1 -r
int copy[nums.size()]; //这里其实可以只保存r-l+1个,但是我懒
for(int k=l;k<=r;k++)
{
if(i==mid+1) //左边用完
{
copy[k]=nums[j];
j++;
//这里不用加了,前面用完了那就是 +0
}
else if(j==r+1) //右边用完
{
copy[k]=nums[i];
i++;
}
else if(nums[i]<=nums[j]) //前面的小
{
copy[k]=nums[i];
i++;
}
else //后面的小
{
copy[k]=nums[j];
j++;
sum+=mid+1-i; //mid-l+1 (一共有几个) - i-l(前面用了) ==mid+1-i(剩下的)
}
}
for(int k=l;k<=r;k++) //返回排好序的子数组
{
nums[k]=copy[k];
}
return sum;
}
int reversePairs(vector<int>& nums) {
if(nums.size()<2) return 0;
return judge(0,nums.size()-1,nums);
}
};