72-归并排序练习-剑指 Offer 51. 数组中的逆序对

题目

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

示例 1:

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

限制:

0 <= 数组长度 <= 50000


思路


代码

class Solution {
    public int reversePairs(int[] nums) {
       return reversePairsInternal(nums, 0, nums.length - 1);
    }

    /**
     * 在nums[l...r]区间中,求逆序对的个数
     * @param nums
     * @param l
     * @param r
     * @return
     */
    private int reversePairsInternal(int[] nums, int l, int r) {
        //终止条件
        if(l >= r) {
            //此时数组只有一个元素,不存在逆序对
            return 0;
        }

        //将数组不断拆分
        int mid = (l + r) >> 1;
        //递归分别求出左半区间和右半区间的逆序对个数
        //左半区间和右半区间都是有序的
        int leftNum = reversePairsInternal(nums, l, mid);
        int rightNum = reversePairsInternal(nums, mid + 1, r);

        if(nums[mid] > nums[mid + 1]) {
            //此时左半区间还有元素 > 右半区间,需要merge
            //整个数组的逆序对个数 = 左区间个数 + 右区间个数 + 本次merge的逆序对个数
            return leftNum + rightNum + merge(nums, l, mid, r);
        } else {
            //此时nums[mid] <= nums[mid + 1]
            //说明此时整个数组已经有序,无需再merge
            //整个数组的逆序对个数就是左区间个数 + 右区间个数
            return leftNum + rightNum;
        }
    }

    /**
     * 归并排序的merge过程,返回当前合并后逆序对的个数
     * @param nums
     * @param l
     * @param mid
     * @param r
     * @return
     */
    private int merge(int[] nums, int l, int mid, int r) {
        int ret = 0;
        int[] temp = new int[r - l + 1];
        //将原数组的元素拷贝到temp
        for (int i = l; i <= r; i++) {
            temp[i - l] = nums[i];
        }

        //i是第一个有序小数组(左半区间)的起始索引
        int i = l;
        //j是第二个有序小数组(右半区间)的起始索引
        int j = mid + 1;

        //开始合并
        //k表示当前处理到原数组的哪个位置
        for (int k = l; k <= r; k++) {
            if(i > mid) {
                //说明左半区间已经全部处理完毕,将右半区间的所有值写回原数组
                nums[k] = temp[j - l];
                j++;
            } else if(j > r) {
                //说明右半区间已经全部处理完毕,将左半区间的所有值写回原数组
                nums[k] = temp[i - l];
                i++;
            } else if(temp[i - l] <= temp[j - l]) {
                //左半区间元素 <= 右半区间元素,此时不构成逆序
                nums[k] = temp[i - l];
                i++;
            } else {
                //左半区间元素 > 右半区间元素,此时构成逆序
                //此时i和j对应的区间中逆序对的个数 = (mid - i + 1)个。
                ret += (mid - i + 1);
                nums[k] = temp[j - l];
                j++;
            }
        }
        return ret;
    }
}

猜你喜欢

转载自blog.csdn.net/WWXDwrn/article/details/130030017