归并排序(前缀和):左组数据比右组数据的两倍大(以及关于整型数据类型的不等式进行变形的反思)

文章讨论了一个数组处理问题,涉及在合并过程中如何计算满足特定条件(左侧元素大于右侧元素两倍)的次数。关键点在于处理整型数据溢出和不等式转化时的精度问题,提出将比较条件中的乘法改为长整型以防止溢出,或者使用浮点数进行除法运算。代码示例展示了如何在Java中实现这一算法优化。
摘要由CSDN通过智能技术生成

题目解析

例如右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;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值