【LeetCode笔记】剑指Offer 51. 数组中的逆序对(Java、分治)

题目描述

  • 多说无益~直接冲代码吧!
    在这里插入图片描述

思路 && 代码

1. 暴力 O( n 2 n^2 n2)

  • 乍一看这题目,很难不直接用暴力法冲一冲(也就双层循环的事)
  • 但是不出意料地超时啦~想一想,O( n 2 n^2 n2)会超时,那么我们就试着思考怎么降低时间复杂度到O(nlogn)吧!(还是不行的话,就再去思考O(n)的方法,循序渐进~)
  • 于是,分治的思路就跃然于纸上了~
class Solution {
    public int reversePairs(int[] nums) {
        // 暴力
        int res = 0;
        int len = nums.length;
        for(int i = len - 1; i > 0; i--) {
            for(int j = i - 1; j >= 0; j--) {
                if(nums[j] > nums[i]) {
                    res++;
                }
            }
        }
        return res;
    }
}

2. 归并排序法(分治O(nlogn))

  • 总体框架应该是和归并排序一样的
  • 从头思考,如果采用分治的方法进行二分,需要有怎样的考虑?
    1. 当前递归层的结果,首先应该是左半边内部的结果 + 右半边内部的结果
    2. 然后,应该再对左半边与右半边的联系进行处理
  • 为什么要排序?
    1. 首先,我们通过二分递归的形式,占用了O(logn)的复杂度
    2. 然后,我们需要使用剩下的O(n)复杂度,完成剩下的步骤
    3. 而排序,占用了O(n)复杂度,同时实现了剩下的步骤
    4. 左半边排序、右半边排序后,并不会影响左右半边直接的联系,也就是无后效性
  • 合并操作
    1. 维护 lowIndex,用于记录插入左半边值后,新增的逆序对。
    2. 插入左半边值后,增加的逆序对 = 已经插入的右边值个数
    3. 注意:左半边当前值、右半边当前值相同的情况,选择插入左半边当前值,这是为了保证正确性。(可以写个例子考虑一下就知道原因了~)
class Solution {
    public int reversePairs(int[] nums) {
        return mergeSort(nums, 0, nums.length - 1);
    }

    public int mergeSort(int[] nums, int left, int right) {
        if(left >= right) {
            return 0;
        }

        // 1. 二分,先获取【合并前局部逆序对】总数
        int mid = (left + right) / 2;
        int res = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);

        // 2. 合并处理
        int i = left, j = mid + 1;
        int[] arr = new int[right - left + 1];
        // 用于处理相等情况
        int lowIndex = 0;
        for(int k = 0; k < arr.length; k++) {
            if(i > mid){
                arr[k] = nums[j++];
            }
            else if(j > right || nums[i] <= nums[j]) {
                res += lowIndex;
                arr[k] = nums[i++];
            }
            else if(nums[i] > nums[j]){
                lowIndex++;
                arr[k] = nums[j++];
            }
        }
        // 把排序数组赋予原数组
        for(i = left; i <= right; i++) {
            nums[i] = arr[i - left];
        }
        return res;
    }
}

二刷

  • 归并排序的思想可太能套用了!
class Solution {
    // 归并排序
    public int reversePairs(int[] nums) {
        return mergeSort(nums, 0, nums.length - 1);
    }
    int mergeSort(int[] nums, int left, int right) {
        if(left >= right) {
            return 0;
        }
        int mid = (left + right) / 2;
        int res = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);

        int[] arr = new int[right - left + 1];
        int first = left, second = mid + 1;
        for(int i = 0; i < arr.length; i++) {
            if(first > mid) {
                arr[i] = nums[second++];
            }
            else if(second > right || nums[first] <= nums[second]) {
                arr[i] = nums[first++];
                res += second - mid - 1;
            }
            else if(nums[second] < nums[first]) {
                arr[i] = nums[second++];
            }
        }

        for(int i = 0; i < arr.length; i++) {
            nums[left + i] = arr[i];
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值