题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
思路
代码
class Solution {
public int reversePairs(int[] nums) {
return reversePairsInternal(nums, 0, nums.length - 1);
}
/**
* 在nums[l...r]区间中,求逆序对的个数
* @param nums
* @param l
* @param r
* @return
*/
private int reversePairsInternal(int[] nums, int l, int r) {
//终止条件
if(l >= r) {
//此时数组只有一个元素,不存在逆序对
return 0;
}
//将数组不断拆分
int mid = (l + r) >> 1;
//递归分别求出左半区间和右半区间的逆序对个数
//左半区间和右半区间都是有序的
int leftNum = reversePairsInternal(nums, l, mid);
int rightNum = reversePairsInternal(nums, mid + 1, r);
if(nums[mid] > nums[mid + 1]) {
//此时左半区间还有元素 > 右半区间,需要merge
//整个数组的逆序对个数 = 左区间个数 + 右区间个数 + 本次merge的逆序对个数
return leftNum + rightNum + merge(nums, l, mid, r);
} else {
//此时nums[mid] <= nums[mid + 1]
//说明此时整个数组已经有序,无需再merge
//整个数组的逆序对个数就是左区间个数 + 右区间个数
return leftNum + rightNum;
}
}
/**
* 归并排序的merge过程,返回当前合并后逆序对的个数
* @param nums
* @param l
* @param mid
* @param r
* @return
*/
private int merge(int[] nums, int l, int mid, int r) {
int ret = 0;
int[] temp = new int[r - l + 1];
//将原数组的元素拷贝到temp
for (int i = l; i <= r; i++) {
temp[i - l] = nums[i];
}
//i是第一个有序小数组(左半区间)的起始索引
int i = l;
//j是第二个有序小数组(右半区间)的起始索引
int j = mid + 1;
//开始合并
//k表示当前处理到原数组的哪个位置
for (int k = l; k <= r; k++) {
if(i > mid) {
//说明左半区间已经全部处理完毕,将右半区间的所有值写回原数组
nums[k] = temp[j - l];
j++;
} else if(j > r) {
//说明右半区间已经全部处理完毕,将左半区间的所有值写回原数组
nums[k] = temp[i - l];
i++;
} else if(temp[i - l] <= temp[j - l]) {
//左半区间元素 <= 右半区间元素,此时不构成逆序
nums[k] = temp[i - l];
i++;
} else {
//左半区间元素 > 右半区间元素,此时构成逆序
//此时i和j对应的区间中逆序对的个数 = (mid - i + 1)个。
ret += (mid - i + 1);
nums[k] = temp[j - l];
j++;
}
}
return ret;
}
}