LeetCode剑指 Offer 51. 数组中的逆序对 (归并排序)

原题链接(官方题解视频讲的很好)

  • 核心思想:在归并排序的过程中计算逆序对的数目
    /**
     * 归并排序
     * @param nums
     * @return
     */
    public int reversePairs(int[] nums) {
        int len = nums.length;
        if(len < 2) return 0;

        int[] copy = new int[len];
        // 如果要求不改变原数组,则需要建立原数组的拷贝
        System.arraycopy(nums, 0, copy, 0, len);

        // 左闭右闭
        // 先new int[len],在递归过程中就不用重复new了,节省大量创建对象的时间
        return reversePairs(copy, 0, len - 1, new int[len]);
    }

    /**
     * 归并排序 + 计算逆序对的个数
     * @param nums
     * @param left
     * @param right
     * @param temp
     * @return
     */
    private int reversePairs(int[] nums, int left, int right, int[] temp){
        if(left == right)  return 0;

        // left + right 可能会导致数值溢出,所以用下面这种方式
        int mid = left + (right - left) / 2;

        int leftPairs = reversePairs(nums, left, mid, temp);
        int rightPairs = reversePairs(nums, mid + 1, right, temp);

        // 如果左分支的数组的数全小于或等于右分支的数组的数,就不用再合并排序了
        if(nums[mid] <= nums[mid + 1])
            return leftPairs + rightPairs;

        int mergeCount = mergeAndCount(nums, left, mid, right, temp);

        return leftPairs + rightPairs + mergeCount;
    }

    /**
     * 合并数组并计算逆序对的数目
     * 数组区间是左闭右闭
     * 参数 nums[left...mid] 和 nums[mid+1 ... right] 都是有序的
     * @param left  左分支的开始
     * @param mid   左分支的结束 (包含在左分支里)
     * @param right 右分支的结束
     * @param temp
     * @return
     */
    private int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp){
        if (right + 1 - left >= 0)
            System.arraycopy(nums, left, temp, left, right + 1 - left);

        int l = left, r = mid + 1, count = 0;
        for(int k = left; k <= right; k++){
            if(l == mid + 1){
                nums[k] = temp[r++];
            } else if(r == right + 1){
                nums[k] = temp[l++];
            }
            // 左大于右,才可以构成逆序对
            else if(temp[l] > temp[r]){
                nums[k] = temp[r++];
                count += mid - l + 1;
            } else {
                nums[k] = temp[l++];
            }
        }

        return count;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值