题解 | #数组中的逆序对#

数组中的逆序对

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放在循环里,就相当于在一边生产一边包装,不断给新生产的辣条(逆序数)腾出地方,保证了最后答案的正确性。

全部评论

相关推荐

不愿透露姓名的神秘牛友
06-23 18:34
点赞 评论 收藏
分享
头顶尖尖的程序员:我也是面了三四次才放平心态的。准备好自我介绍,不一定要背熟,可以记事本写下来读。全程控制语速,所有问题都先思考几秒,不要急着答,不要打断面试官说话。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务