题目解析
例如右arr[5,1,2,3],有多少种情况使得数组中左边的数比右边的数大的两倍还大,如其中一种情况:5 > 1 * 2
题解分析
在merge()的过程中,p1作为左数组的指针,p2作为右数组的指针,p1的初始值为l,p2的初始值为mid + 1,计算一次merge()产生的满足题设的情况的数量。
for (int j = p1; j <= mid; j++) {
while (w <= r && arr[j] > arr[w] * 2) {
w++;
}
sum += w - mid - 1;
}
以上代码中,w作为一次merge()过程的滑动窗口,此设计来源于merge()的过程中,左数组和右数组都各自具有单调性,w在右数组上滑动,当p1向右一位,曾经使w滑动的数据仍旧满足于新的p1所指向的数据。
值得注意的地方(非常坑,真的很坑)
以上代码中while中的判断条件,arr[j] > arr[w] * 2中,arr[w]是整型数据,这个地方如果arr[w]特别大的话,arr[w]乘以2之后会溢出变成负数,因此这个地方需要对arr[j]和arr[w] * 2转化为long类型。
如果不想转化为long类型,首先想到的就是将不等式转化为arr[j] / 2 > arr[w],然后…这个算法就不能通过了,最初看了代码两小时也没发现这个这个问题,后来意识到,int类型的数据除以2之后是向下取整的,比如:arr[j]是3,arr[w]是1,满足arr[j] > arr[w] * 2,但是arr[j] / 2算出来是1,不满足于arr[j] / 2 > arr[w],因此arr[j]是3,arr[w]是1的这种情况如果用arr[j] / 2 > arr[w]这个不等式将不会被统计到最终结果,于是就出现了偏差。
其实arr[j] / 2 > arr[w]这个思路来避免溢出也可以实现,但是需要将arr[j] / 2和arr[w]转化为float类型。
代码
public static int reversePairs2(int[] arr) {
if (arr == null || arr.length < 2)
return 0;
return process1(arr, 0, arr.length - 1);
}
public static int process1(int[] arr, int l, int r) {
if (l == r) {
return 0;
}
int mid = l + ((r - l) >> 1);
return process1(arr, l, mid) + process1(arr, mid + 1, r) + merge1(arr, l, mid, r);
}
public static int merge1(int[] arr, int l, int mid, int r) {
int sum = 0;
int p1 = l;
int p2 = mid + 1;
int i = 0;
int[] h = new int[r - l + 1];
int w = mid + 1;
for (int j = p1; j <= mid; j++) {
while (w <= r && (long)arr[j] > (long)arr[w] * 2) {
w++;
}
sum += w - mid - 1;
}
while (p1 <= mid && p2 <= r) {
h[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= mid) {
h[i++] = arr[p1++];
}
while (p2 <= r) {
h[i++] = arr[p2++];
}
for (int j = 0; j < h.length; j++) {
arr[l + j] = h[j];
}
return sum;
}