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

Posted by Kaka Blog on November 10, 2020

题目链接

题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

参考答案:

class Solution {
    public int reversePairs(int[] nums) {
        int length = nums.length;
        // 少于2个元素直接返回0,不存在逆序对
        if (length < 2) {
            return 0;
        }
        int[] temp = new int[length];
        int ans = reversePairs(nums, 0, length-1, temp);
        // System.out.println(Arrays.toString(nums));
        return ans;
    }

    /**
     * nums[left..right] 计算有序数组里的逆序对个数
     **/
    public int reversePairs(int[] nums, int left, int right, int[] temp) {
        if (left == right) {
            return 0;
        }
        // 不用(left + right)/2,防止整型溢出
        int mid = left + (right - left) / 2;
        int leftCount = reversePairs(nums, left, mid, temp);
        int rightCount = reversePairs(nums, mid+1, right, temp);
        // 已经是有序数组,直接返回两个数组的逆序对
        if (nums[mid] < nums[mid+1]) {
            return leftCount + rightCount;
        }
        int crossCount = mergeAndCount(nums, left, mid, right, temp);
        return leftCount + rightCount + crossCount;
    }

    public int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp) {
        // temp是辅助数组
        for (int i=left; i<=right; i++) {
            temp[i] = nums[i];
        }
        int i = left;
        int j = mid + 1;
        int count = 0;
        // 合并temp[i..mid]和temp[mid..right]数组到nums[left..right]
        for (int k=left; k<=right; k++) {
            if (i == mid + 1) {
                nums[k] = temp[j];
                j++;
            }
            else if (j == right + 1) {
                nums[k] = temp[i];
                i++;
            }
            else if (temp[i] <= temp[j]) {
                nums[k] = temp[i];
                i++;
            }
            else if (temp[i] > temp[j]) {
                nums[k] = temp[j];
                j++;
                // 第二个区间小于第一区间,逆序对个数等于第一区间剩下的元素个数
                count += (mid - i + 1);
            }
        }
        return count;
    }
}

执行用时:34 ms

思路

采用归并排序算法的思路,归并两个有序数组时,当需要归并右边的数组时,这时候它的逆序对个数等于左边数组待归并的数量,再加上左右两个数组的逆序对数量,就是最终结果。按照分治思想,左右两个数组的逆序对可以用同样的算法求得。

参考资料