题解 | #数组中的逆序对#
数组中的逆序对
https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5
import java.util.*;
public class Solution {
int count = 0;
public int InversePairs(int [] array) {
// 长度小于2则无逆序对
if (array.length < 2)
return 0;
// 进入归并
mergeSort(array, 0, array.length - 1);
return count;
}
public void mergeSort(int[] array, int left, int right) {
// 找分割点
int mid = left + (right - left) / 2;
if (left < right) {
// 左子数组
mergeSort(array, left, mid);
// 右子数组
mergeSort(array, mid + 1, right);
// 并
merge(array, left, mid, right);
}
}
public void merge(int[] array, int left, int mid, int right) {
// 创建临时数组,长度为此时两个子数组加起来的长度
int[] arr = new int[right - left + 1];
// 临时数组的下标起点
int c = 0;
// 保存在原数组的起点下标值
int s = left;
// 左子数组的起始指针
int l = left;
// 右子数组的起始指针
int r = mid + 1;
while (l <= mid && r <= right ) {
// 当左子数组的当前元素小的时候,跳过,无逆序对
if (array[l] <= array[r]) {
// 放入临时数组
arr[c] = array[l];
// 临时数组下标+1
c++;
// 左子数组指针右移
l++;
} else { // 否则,此时存在逆序对
// 放入临时数组
arr[c] = array[r];
// 逆序对的个数为 左子数组的终点- 当前左子数组的当前指针
count += mid + 1 - l;
count %= 1000000007;
// 临时数组+1
c++;
// 右子数组的指针右移
r++;
}
}
// 左子数组还有元素时,全部放入临时数组
while (l <= mid)
arr[c++] = array[l++];
// 右子数组还有元素时,全部放入临时数组
while (r <= right)
arr[c++] = array[r++];
// 将临时数组中的元素放入到原数组的指定位置
for (int num : arr) {
array[s++] = num;
}
}
}
这道题中有三个晦涩的地方:
1,使用归并排序,求出逆序方法,要用到递归的思想
2,在合并临时数组的时候,如果左数组的当前值大于了右数组的当前值,那么左数组余下的个数就是逆序数,因为左数组已经是升序的了
3,在循环中不断count %= 1e9+7, 也就是count %= 1000000007时,最后跟一次性count %= 1000000007是一样的
很多人在3这点卡住,但我想了一个“辣条包装生产线”的例子,希望能帮到大家:
首先要明白,做count %= 1e9+7是为了把一个很大的数限制在int类型的范围内,在这里count是数组里的逆序数,当数组很大很复杂时,这个数可能非常大,所以要这么处理,在这里也是为了教会我们这个可以用在以后代码里的小trick。
为了理解它,我们开始打个比方。count += mid + 1 - l 是用来计算出逆序数并且累加的,这就像是一个不断地产生辣条的机器,而产出的辣条会累积起来放在count里。然后我们规定每一包里要装1e9+7根辣条,这就相当于 count %= 1e9+7,如果我们最后剩下来的不足1e9+7根,也就是不够一包,那么我们就把剩余的放回count里,也就是等待下一批做好的辣条,然后混在一起继续包装(下一个循环)。
在现实中,这个跟一次性把所有的原料都做成辣条(对应把整个数组的逆序数算出来),然后再一次性包装count %= 1e9+7(对应P mod 1000000007)实质上是一样的,最后都会剩下一样多的辣条存在count里。而关键就在java里,count作为一个int类型的数据,大小是有限制的,辣条并不能无限地堆放在count里,如果在生产的过程中(计算整个数组的逆序数的过程中)累加的count超过了int的限制,那么就会因为溢出而导致最后答案出错,而如果把count %= 1e9+7放在循环里,就相当于在一边生产一边包装,不断给新生产的辣条(逆序数)腾出地方,保证了最后答案的正确性。