牛客 BM20 数组中的逆序对 【二分排序】

描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007
数据范围: 对于
50% 的数据, size 10^4
对于100% 的数据 size ≤ 10^5

数组中所有数字的值满足 0 ≤ val ≤ 10^9

要求:空间复杂度O(n),时间复杂度 O(nlogn)

输入描述:

题目保证输入的数组中没有的相同的数字

示例1

输入:

[1,2,3,4,5,6,7,0]

返回值:

7

示例2

输入:

[1,2,3]

返回值:

0

解决方法:

通过冒泡排序,一个数字前面有几个大于它的数,等于它从小到大排序向前移动了几位,也就是冒泡的次数。因为是顺序冒泡,所以前面已经冒泡过的小数或相同数不会干扰当前数字的冒泡次数,所以找逆序对次数的问题就转换成了升序排序冒泡次数的问题。

    public int InversePairs(int [] array) {
        if (array == null || array.length == 0 || array.length == 1) {
            return 0;
        }
        int total = 0;
        for (int i = 1; i < array.length; i++) {
            if (array[i] < array[i-1]) {
                total += popSort(array, i);
                total %= 1000000007;
            }
        }
        return total;
    }

    public int popSort(int[] nums, int pos) {
        int temp;
        int total = 0;
        while (pos > 0 && nums[pos] < nums[pos-1]) {
            temp = nums[pos];
            nums[pos] = nums[pos-1];
            nums[pos-1] = temp;
            total++;
            pos--;
        }
        return total;
    }

优化:冒泡排序太慢了,整体时间复杂度为 O(N^2) ,所以需要对冒泡进行优化,所以可以采用二分排序来优化这一过程,使时间复杂度下降到 O(NlogN)。

    public int InversePairs(int [] array) {
        if (array == null || array.length == 0 || array.length == 1) {
            return 0;
        }
        return dcSort(array, 0, array.length-1, new int[array.length]);
    }

    // 递归排序
    public int dcSort(int[] array, int start, int end, int[] temp) {
        if (start >= end) {
            temp[0] = array[start];
            return 0;
        }
        int total = 0;
        int mid = (start + end) / 2;
        int[] left = new int[mid - start + 1];
        int[] right = new int[end - mid];
        total += dcSort(array, start, mid, left);
        total %= 1000000007;
        total += dcSort(array, mid+1, end, right);
        total %= 1000000007;
        total += mergeArray(temp, left, right);
        total %= 1000000007;
        return total;
    }

    // 合并左右时计算逆序对个数
    public int mergeArray(int[] result, int[] left, int[] right) {
        int pos = 0;
        int lLen = left.length;
        int total = 0;
        int lPos = 0;
        int rPos = 0;
        while (lPos < left.length && rPos < right.length) {
            if (left[lPos] > right[rPos]) {
                result[pos] = right[rPos];
                rPos++;
                total += (lLen - lPos);
                pos++;
            } else {
                result[pos++] = left[lPos++];
            }
        }
        while (lPos < left.length) {
            result[pos++] = left[lPos++];
        }
        while (rPos < right.length) {
            result[pos++] = right[rPos++];
        }
        return total;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值