逆序对问题详情

82 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目

在一个数组中, 左边的数如果比右边的数大, 则这两个数构成一个 ,请问有多少个逆序对。

题目思路

这个问题是用到归并的思想。

那为什么会想到这个呢?

我们可以先想想归并是怎么样的。

我们看举例来说明吧

举例

数组1:【2,4,8】

数组2:【1,5,6】

创建一个新的数组help【0,0,0,0,0,0】

定义一个变量(ans)计算有多少对逆序对

上面的数组是由一个数组中分为了两个部分的数组。

我们先选中数组1的第一个元素

选中数组2的第一个元素

我们比较之后发现数组1的第一个元素大于数组2的第一个元素

我们先把数组2的第一个元素拷贝上help上面

因为数组1是在数组2的左边,那就说明2 是在 1 的左边的,正常来说2应该是在1的右边的,所以这就是逆序对。那ans可以加多少呢?我们看到数组1还有3个,这说明,数组1还有三个数是大于1的,那就说明关于1的逆序对有3对。

那么如果数组1的数字比数组2的数字小的时候怎么办呢?

那就正常拷贝上去,不用操作,因为那个不符合逆序对的原则

如果相同的时候就拷贝数组1的元素。

代码

我们还是按照老规矩,先放merge函数

public static int merge(int[] arr, int l, int m, int r) {
    int[] help = new int[r - l + 1];
    int cur1 = l;
    int cur2 = m + 1;
    int index = 0;
    int ans = 0;
    while(cur1 <= m || cur2 <= r){
        int num1 = cur1 <= m ? arr[cur1] : Integer.MAX_VALUE;
        int num2 = cur2 <= r ? arr[cur2] : Integer.MAX_VALUE;
        if(num1 <= num2){
            help[index++] = arr[cur1++];
        }else{
            ans += (m - cur1 + 1);
            help[index++] = arr[cur2++];
        }
    }
    for(int i = 0;i<help.length;i++){
        arr[i + l] = help[i];
    }
    return ans;
}

这个merge就是根据上面的来的。大家可以看看。

public static int process(int[] arr, int l, int r) {
    if (l == r) {
        return 0;
    }
    // l < r
    int mid = l + ((r - l) >> 1);
    int left = process(arr, l, mid);
    int right = process(arr, mid + 1, r);
    int cur = merge(arr, l, mid, r);
    return left + right + cur;
}

这个是关于分割小部分的递归函数,和上面的都是一样的。

调用函数也是一样的简单

public static int reversePairNumber(int[] arr){
    if (arr == null || arr.length < 2) {
        return 0;
    }
    return process(arr, 0, arr.length - 1);
}

这样逆序对的问题就解决了。