leetcode_jzoffer_51
分析:
1.arr[j]<arr[i],则arr[j]<从i开始到mid的所有左区间元素,构成了逆序对,则有mid-i+1个逆序对
2.在归并排序调序的过程中(移动i,j的过程中)顺便统计了逆序对。逆序对只可能出现在merge过程,右区间元素<左区间元素时才构成逆序对。
3.merge操作:合并数组的两个有序区间为新的有序区间,返回合并过程中顺便统计到的逆序对个数。
代码实现:
public class JZ51_ArrayReversePair {
public int InversePairs(int [] array) {
return reversePairsHelper(array,0,array.length-1);
}
/**
* 传入一个数组nums,就可以求出nus[l...r]上的逆序对个数
* @param nums
* @param left
* @param right
* @return 此时nums[l...r]逆序对个数
*/
private int reversePairsHelper(int[] nums, int left, int right) {
if(left>=right){
//区间只剩一个元素了,不构成逆序对
return 0;
}
int mid=left+((right-left)>>1);
//先递归求出左区间逆序对
int leftPairs=reversePairsHelper(nums,left,mid);
//再求出右区间逆序个数
int rightPairs=reversePairsHelper(nums,mid+1,right);
//当左右区间没有排序好,进入merge求其中的逆序对个数,否则左右已排序好merge中就没有逆序对,直接返回leftPairs+rightPairs
if(nums[mid]>nums[mid+1]){
//左区间最后的元素>右区间起始元素,未排序好
return merge(nums,left,mid,right)+leftPairs+rightPairs;
}
//此时左右区间有序,整个集合也有序
return leftPairs+rightPairs;
}
/**
* 合并nums的两个有序区间[l...mid][mid+1...r]
* @param nums
* @param left
* @param mid
* @param right
* @return 合并过程中逆序对的个数
*/
private int merge(int[] nums, int left, int mid, int right) {
int[] aux=new int[right-left+1];
//合并过程中产生的逆序对个数
int res=0;
for (int i = 0; i < aux.length; i++) {
aux[i]=nums[i+left];//nums有left个单位的偏移量
}
//左右两个小数组开始位置的索引
int i=left;
int j=mid+1;
//k表示当前正在合并的原数组的索引下标
for (int k = left; k <=right; k++) {
if(i>mid){//左侧合并完了,放入右区间元素
nums[k]=aux[j-left];
j++;
}else if(j>right){//右侧小数组遍历完了,左侧放入
nums[k]=aux[i-left];
i++;
}else if(aux[i-left]<=aux[j-left]){
//不构成逆序对,直接左侧元素覆盖nums[k]
nums[k]=aux[i-left];
i++;
}else{
//右区间元素>左区间元素,构成逆序对
//此时逆序对个数:mid-i+1(j小于i说明小于i到mid之间所有元素【i->mid的元素有序】)
res+=(mid-i)+1;
res %= 1000000007;
//右边小于左边,右边元素往nums[k]上覆盖
nums[k]=aux[j-left];
j++;
}
}
return res;
}
}