数组排序篇 -- 数组中的逆序对

题目描述

牛客网:JZ35 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
示例

输入:[1,2,3,4,5,6,7,0]
输出:7

题目解析

方法一:暴力破解

根据题意,前面数字大于后面数字即为一个逆序对,两层遍历数组可以得到所有逆序对。
注意:当数据量较大时,int数据不足以表示所有结果,因此计算时使用long型整数。

public int InversePairs(int [] array) {
        long p = 0;
        for(int i = 0;i < array.length;i++){
            for(int j = i + 1;j < array.length;j++){
                if(array[i] > array[j]){
                    p++;
                }
            }
        }
        return (int)(p % 1000000007);
    }
  • 时间复杂度为:O(n2)
    对于105 的数据来说可以运行成功,但数据量再大就容易超时。
  • 空间复杂度为:O(1)

方法二:归并排序

解法参考:官方题解

  1. 归并排序思想
    归并排序的思想是将数组分为相等的两个子数组,分别进行排序后再进行合并。
    通过递归方法将子数组划分至长度为1,最终完成整个数组的排序和合并。
    时间复杂度为:O(nlog2n)
    空间复杂度为:O(n)

  2. 逆序对求解
    考虑这种情况:[4,5,3]和[2,1]
    两个子数组要进行排序后合并,则区间之间的逆序对为:

    • 排序前:(4,2)(4,1)(5,2)(5,1)(3,2)(3,1)
    • 排序后区间为[3,4,5]和[1,2],逆序对结果不变

    对于当左侧区间的首元素大于右侧区间某元素时,左侧区间的其他元素也大该元素。如3 > 1,则有4 > 15 > 1。因此,对于排序后的子区间来说,若左侧区间首元素有一个逆序对,则左侧区间其他元素也都有了一个逆序对

    根据此思路对归并排序算法进行改进,在排序过程中得到逆序对数量。

public int InversePairs(int [] array) {
        int ans = 0;
        // 将逆序对作为参数加入到排序过程中
        ans = mergeSort(array,0,array.length - 1,ans);
        return ans;
    }
    public int mergeSort(int[] array,int left,int right,int ans){
        // 区间只有一个数,不用排序,直接返回
        if(left >= right){
            return ans;
        }
        // 将区间分为两个子区间
        int mid = left + ((right - left) >> 1);
        ans = mergeSort(array,left,mid,ans);
        ans = mergeSort(array,mid + 1,right,ans);
        // 合并两个子区间
        ans = merge(array,left,mid,right,ans);
        return ans;
    }
    public int merge(int[] array,int left,int mid,int right,int ans){
        // 临时数组,存储排序合并后的子区间
        int[] temp = new int[right - left + 1];
        int i = left,j = mid + 1,k = 0;
        
        while(i <= mid && j <= right){
            if(array[i] > array[j]){
                temp[k++] = array[j++];
                // 左侧区间大于右边的数
                ans += (mid - i + 1);
                ans %= 1000000007;
            }else{
                temp[k++] = array[i++];
            }
        }
        while(i <= mid){
            temp[k++] = array[i++];
        }
        while(j <= right){
            temp[k++] = array[j++];
        }
        
        for(int m = 0,n = left;n <= right;n++,m++){
            array[n] = temp[m];
        }
        return ans;
    }
  • 时间复杂度为:O(nlog2n)
  • 空间复杂度为:O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值