题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
例子:
代码1(分治法):
class Solution {
public int reversePairs(int[] nums) {
int len = nums.length;
//数组长度太小,直接返回0
if (len < 2){
return 0;
}
//初始化copy数组并拷贝nums数组得值,目的是为了不损坏源数组。
int[] copy = new int[len];
for (int i = 0; i <= len - 1; i++){
copy[i] = nums[i];
}
//定义一个辅助数组。
int[] temp = new int[len];
int totalCount = reverse(copy, 0, len - 1, temp);
return totalCount;
}
private int reverse(int[] copy, int left, int right, int[] temp) {
if (right <= left){
return 0;
}
//下取整,可能遇到溢出问题,注意,这里是+号 int mid = (right + left) / 2,所以换了另一种方法。
//错误的示例,注意跟下面的区别:int mid = (left + (right - left)) / 2;
int mid = left + (right - left) / 2;
//计算左边右边和交叉的逆序对并相加返回。
int left_pair = reverse(copy, left, mid, temp);
int right_pair = reverse(copy, mid + 1, right, temp);
//如果两个子数组有序,那么直接返回。
if (copy[mid] < copy[mid + 1]){
return left_pair + right_pair;
}
int cross_pair = countReverse(copy, left, mid, right, temp);
return left_pair + right_pair + cross_pair;
}
private int countReverse(int[] copy, int left, int mid, int right, int[] temp) {
//之所以不new一个新数组,第一浪费空间,第二还要解决下标漂移的问题。
for (int k = left; k <= right; k++){
temp[k] = copy[k];
}
//i,j分别指向两段有序数组的第一个元素,并开始比较。
int i = left;
int j = mid+1;
int count = 0;
for (int k = left; k <= right; k++){
//做判断,看是否一边全部遍历完成。
if (i == mid + 1){
copy[k] = temp[j];
j++;
}else if (j == right + 1){
copy[k] = temp[i];
i++;
} else if (temp[i] <= temp[j]){ //加上等号,可以避免归并排序的不稳定性。
copy[k] = temp[i];
i++;
}else {
copy[k] = temp[j];
j++;
count += (mid - i) + 1;
}
}
return count;
}
}
代码1详解:
其实就是一个递归,同时归并排序正好也是这样的形状,在归并排序的基础上加一个统计逆序数的就ok了。
这样图清晰的反映了怎么统计逆序数。
附上官方视频讲解:数组中的逆序数