//在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
//暴力方法超时,考虑归并排序
class Solution {
public int reversePairs(int[] nums) {
if(nums==null||nums.length<2){
return 0;
}
int len=nums.length;
int []copy=new int[len];
//通常输入不许改变,复制一份
for(int i=0;i<len;i++){
copy[i]=nums[i];
}
//辅助数组
int []temp=new int[len];
//归并排序
return reversPairs(copy,0,len-1,temp);
}
public int reversPairs(int []nums,int left,int right,int []temp){
if(left==right){
return 0;
}
int mid=(left+right)/2;
//左边逆序数
int leftcount=reversPairs(nums,left,mid,temp);
//右边逆序数
int rightcount=reversPairs(nums,mid+1,right,temp);
//如果左边最后一个小于等于右边第一个,因为左右都排序了,所以左边全体小于右边,没有跨区间逆序数,直接返回两边的逆序数
if(nums[mid]<=nums[mid+1]){
return leftcount+rightcount;
}
//计算合并产生逆序数
int mergeCount=merge(nums,left,mid,right,temp);
//返回两边+合并逆序数
return mergeCount+leftcount+rightcount;
}
public int merge(int []nums,int left,int mid,int right,int []temp){
for(int i=left;i<=right;i++){
temp[i]=nums[i];
}
//i指针指向左半边起始
int i=left;
//j指针指向右半边起始
int j=mid+1;
int count=0;
for(int k=left;k<=right;k++){
//左半边遍历完毕,把右半边剩下的放入
if(i==mid+1){
nums[k]=temp[j];
j++;
}else if(j==right+1){
//右半边遍历完毕,把左半边剩下的放入
nums[k]=temp[i];
i++;
}else if(temp[i]<=temp[j]){
//左边指针位置小于右边,不产生逆序数,直接把指针位置放回数组,指针向前移动
nums[k]=temp[i];
i++;
}else{
//右半边指针位置较小,因为经过排序,那么左半边从i到末尾的mid的数全大于j位置的数,j位置逆序数为mid-i+1
//也就是i到mid全部数字个数
nums[k]=temp[j];
j++;
count=count+(mid-i+1);
}
}
return count;
}
}