《剑指offer》刷题——【时间效率与空间效率的平衡】面试题51:数组中的逆序对(java实现)

《剑指offer》刷题——【时间效率与空间效率的平衡】面试题51:数组中的逆序对(java实现)

一、题目描述

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

二、题目分析

方法一:遍历-O(n^2)

  • 顺序扫描整个数组,
  • 每扫描一个数字,逐个比较该数字和它后面的数字的大小
  • 若后面的数字比它小,则这两个数字就组成一个逆序对

方法二:归并思想,时间负责度O(nlogn),空间复杂度O(n)

  • 将数组分成前后两个子数组,
  • 统计子数组内部的逆序对数目,
  • 统计出两个相邻子数组之间的逆序对的数目
    在这里插入图片描述
    在这里插入图片描述
  • 合并、排序并统计逆序对过程:
    • 用两个指针分别指向两个子数组的末尾,每次比较指针指向数字
    • 每次比较,都把较大的数字从后往前复制到一个辅助数组,确保辅助数组中的数字是递增排序,再把较大的数字复制到辅助数组后,把辅助数组的指针向前移动一位,进行下次比较
    • 若第一个子数组中数字 > 第二个子数组中数字,则构成逆序对,逆序对数=第二个子数组中剩余数字个数
    • 若第一个子数组中数字 < 第二个子数组中数字,则不构成逆序对

三、代码实现

public class Solution{
    private int count = 0;

    public int InversePairs(int[] a) {
        //无效输入
        if (a == null || a.length == 0)
            return -1;
        //归并排序
        mergeSort(a, 0, a.length - 1);
        //返回逆序对数
        return count;
    }
    /**
    *归并排序
    */
    public void mergeSort(int[] a, int left, int right) {
        //递归分割数组
        if (left < right) {
            int mid = (left + right) / 2; //中值
            mergeSort(a, left, mid);//左子序列归并排序
            mergeSort(a, mid + 1, right);//右子序列归并排序
            merge(a, left, mid, right);//按序归并
        }
    }

    public void merge(int[] a, int left, int mid, int right) {
        //额外空间
        int[] tmp = new int[right - left + 1];
        int t = right - left;//临时数组下标
        int l = mid;//前半段索引,初始化为前半段最后一个数字的下标
        int r = right;//后半段索引,初始化为后半段最后一个数字的下标
        while (l >= left && r >= mid + 1) {
            //从两段最后一个元素开始比较,将两个数字中大的从后往前放到临时数组中
            //若第一个子数组中数字 >  第二个子数组中数字,则构成逆序对,
            //逆序对数=第二个子数组中剩余数字个数
            if (a[l] > a[r]) {
                count += (r - mid);
                tmp[t--] = a[l--];
                if (count >= 1000000007) {
                    count %= 1000000007;
                }
            } 
            若第一个子数组中数字 < 第二个子数组中数字,则不构成逆序对
            else {
                tmp[t--] = a[r--];
            }
        }
        //若右子序列已全部存到临时数组,将左子序列从后往前按序插入辅助数组
        while (l >= left) {
            tmp[t--] = a[l--];
        }
        //若右子序列已全部存到临时数组,将右子序列从后往前按序插入辅助数组
        while (r >= mid + 1) {
            tmp[t--] = a[r--];
        }
        //复制排好序的数字到原数组中
        for (int i = 0; i <= right - left; i++) {
            a[left + i] = tmp[i];
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值