剑指Offer 51、数组中的逆序对

238 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

题目:数组中的逆序对指的是在一个数组中,前面的元素数值大于后面的元素,则构成逆序对,先要求求出给定数组的逆序对总数。

解题思路

按照常规模拟,数组中的前一个数大于后一个数则可构成逆序对,简单的想法是通过双层for循环来查看所有情况,当满足条件时则逆序对数量自增,可得代码如下:

public int reversePairs(int[] nums) {
    int count = 0;
    for(int i=0;i<nums.length-1;i++){
        int value = nums[i];
        for(int j=i+1;j<nums.length;j++){
            if(nums[j]<value) count++;
        }
    }
    return count;
}

这种方法的时间复杂度为O(n2)O(n^2),但此题为hard,显然不会这么简单,并且最终测试超时。再次观察数组,要达到比O(n2)O(n^2)小的时间复杂度只可能有O(n)O(n)O(nlogn)O(nlogn),对于本题来说,O(n)O(n)显然是不可能的,那也只能是O(nlogn)O(nlogn)了,要达到此时间复杂度必然需要对数组进行二分,二分的思想目前了解的就是二分查找和归并排序了,甚至快速排序思想也和这个类似。但如何利用这个思想解答本题呢?

我们可以观察如下序列7, 6, 5, 4, 3, 2, 1,这是一个完全逆序的序列,此序列的逆序对总共为(76)/2=21(7*6)/2=21,计算公式即为(6+5+4+3+2+1)/2(6 + 5 + 4 + 3 + 2 + 1) / 2,如果我们对这个序列进行排序,那么7在6前面,则7和6必然构成逆序对,如果6和7排序完成,而5小于6和7,则5进行排序必然会产生两个逆序对,依次类推,此思想是在序列进行排序时计算逆序对,排序的方式为归并排序,归并排序每次二分,直到每个数组元素个数都为1的时候停止划分,此时进行两两合并,如果左边数组元素大于右边则构成逆序对,并且逆序对的个数即为左边数组的元素个数(满足此结果的条件的左右两边数组都相对有序,归并排序刚好可以满足),通过归并排序可得最终代码如下:

public int reversePairs(int[] nums) {
    if(nums.length<2) return 0;
    return mergeSort(nums, 0, nums.length-1);
}

public int mergeSort(int[] nums, int left, int right){
    if(left<right){
        int mid = left + (right-left)/2;
        int l = mergeSort(nums, left, mid);
        int r = mergeSort(nums, mid+1, right);
        int m = merge(nums, left, mid, right);
        return l+r+m;
    }else {
        return 0;
    }
}

public int merge(int[] nums, int left, int mid, int right){
    int count = 0;
    int[] res = new int[right - left + 1];
    int l1 = left, l2 = mid+1, l3 = 0;
    while(l1<=mid&&l2<=right){
        if(nums[l1]<=nums[l2]){
            res[l3++] = nums[l1++];
        }else {
            res[l3++] = nums[l2++];
            count += (mid-l1+1);
        }
    }
    while(l1<=mid){
        res[l3++] = nums[l1++];
    }
    while(l2<=right){
        res[l3++] = nums[l2++];
    }
    for(int i=0;i<res.length;i++){
        nums[left+i] = res[i];
    }
    return count;
}