剑指offer-数组中的逆序对

题目描述

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

问题分析

  • 暴力法:两层循环统计出逆序对,时间复杂度:O(N^2),数据量大时会超时
  • 改写归并排序,利用归并排序中的分治思想,在两个有序的子数组进行merge合并操作时,统计出逆序对的数量注意,此时子数组已经是有序的了,而原子数组内部的逆序对已经统计完了。也可以这么想,递归一步步深入下去,最终子数组只包含一个元素,然后在merge过程中,完成子数组的排序(只有两个元素),并且统计出逆序对,再一步步返回。
    • 那么如何在merge过程中统计出两个已经排好序的子数组中的逆序对呢
      比如现有两个已经排好序的子数组[3,6,7] 和[2,4,5],两者在原数组中的形式是[3,6,7,2,4,5],那么在进行merge过程中,i 指向第一个子数组的开始处,mid 指向 第一个子数组的结尾处, j 指向第二个子数组的开始处,比较 arr[i] 与 arr[j],如果 arr[j] < arr[i] ,说明存在逆序对,并且 【i ~ mid】都大于 arr[j], 所以对于 arr[j]而言,它能产生的逆序对个数为 mid - i + 1 个,然后将 j后移。如果 arr[i] < arr[j] ,那么说明arr[j] 不会产生逆序对。
  • 总结:若想统计数组【begin ~ end】中的逆序对,同样用分治的思想

    • 统计 【begin ~ mid】中的子数组内部逆序对 num1,并在统计过程中排序(递归)
    • 统计【mid + 1 ~ end】中的子数组内部逆序对 num2,并在统计过程中排序(递归)
    • 统计已经排好序的【begin ~ mid】 与【mid + 1 ~ end】在merge时子数组之间的逆序对
    • 前三者相加,便是【begin ~ end】中的逆序对,其实也完成了排序

    但要注意,统计逆序对,其实都发生在merge过程中

时间复杂度:O(NlogN)

经验教训

  • 归并排序
  • 分治的思想

代码实现

public class Solution {
    public int InversePairs(int [] array) {
        if (array == null || array.length == 0) {
            return 0;
        }
        return (int)(mergeSort(array, 0, array.length - 1) % 1000000007);
    }

    public long mergeSort(int[] arr, int begin, int end) {
        if (begin == end) {
            return 0;
        }
        int mid = begin + ((end - begin) >> 1);
        long num1 = mergeSort(arr, begin, mid);
        long num2 = mergeSort(arr, mid + 1, end);
        long num3 = merge(arr, begin, mid, end);
        return num1 + num2 + num3;
    }

    public long merge(int[] arr, int begin, int mid, int end) {
        int[] temp = new int[end - begin + 1]; 
        int i = begin;
        int j = mid + 1;
        int k = 0;
        long count = 0;
        while (i <= mid && j <= end) {
            if (arr[i] > arr[j]) {
                count += mid - i + 1;
                temp[k++] = arr[j++];
            }else {
                temp[k++] = arr[i++];
            }
        }
        while (i <= mid) {
            temp[k++] = arr[i++];
        }
        while (j <= end) {
            temp[k++] = arr[j++];
        }
        i = 0;
        while (i < end - begin + 1) {
            arr[begin + i] = temp[i];
            i++;
        }
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值