参考:1、 https://www.geeksforgeeks.org/merge-sort/

2、《剑指Offer:名企面试官精讲典型编程题》

题目描述

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

输入描述:

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

数据范围:

  • 对于%50的数据,size<=10^4
  • 对于%75的数据,size<=10^5
  • 对于%100的数据,size<=2*10^5
示例1
输入:1,2,3,4,5,6,7,0
输出:7

解题思路1 (暴力法,时间复杂度: O(n^2))

这个方法最简单直接,顺序扫描整个数组,每扫到一个数,逐个比较其后面的所有数,如果构成逆序对,则计数+1。代码没啥难度,略了。

解题思路2 (分治法,时间复杂度: O(nlogn),空间复杂度:O(n))

     public int InversePairs(int[] array) {
         if (array.length < 2) {
             return 0;
         }
         return divideAndConquer(array, 0, array.length);
     }

     public int divideAndConquer(int[] array, int l, int r) {
         if (l >= r - 1) return 0;

         int mid = (l + r) / 2;
         int count = 0;
         int countL = divideAndConquer(array, l, mid);
         int countR = divideAndConquer(array, mid, r);
         int[] left = Arrays.copyOfRange(array, l, mid);
         int[] right = Arrays.copyOfRange(array, mid, r);
         int i = left.length - 1;
         int j = right.length - 1;
         for (int k = r - 1; k >= l; k--) {
             // two cases: left is empty or right is empty
             if (i < 0) {
                 array[k] = right[j];
                 j--;
                 continue;
             } else if (j < 0) {
                 array[k] = left[i];
                 i--;
                 continue;
             }
             if (left[i] > right[j]) {
                 count += j + 1;
                 if (count > 1000000007) {
                     count %= 1000000007;
                 }
                 array[k] = left[i];
                 i--;
             } else {
                 array[k] = right[j];
                 j--;
             }
         }
         return (count + countL + countR) % 1000000007;
     }

这一题的解法是在归并排序(Merge Sort) 的基础上修改得到的。Merge Sort使用的是分治法的思想。分治法 (divide and conquer) 是将一个复杂的问题,分成两个或多个相同或者相似的子问题,再把子问题分成更小的子问题,一直分到子问题足够简单来求解,最后再把子问题的解合并成原问题的解。归并排序法的时间复杂度为 O(nlogn),空间复杂度为 临时的数组 + 递归时压入栈的数据占用的空间 = n + logn,即 O(n)。

以数组 {7, 5, 6, 4} 为例进行分析,解题过程如图5.2所示。首先将数组拆成至长度为1的子数组,然后一边合并一边统计逆序对的数目。在第一对长度为1 的子数组 {7}、{5}中,有一对逆序对 (7, 5)。在第二对长度为1的子数组 {6}、{4}中,也有一对逆序对(6, 4)。在统计完这两对逆序对后,需要分别对他们进行排序,如图 5.2(c),以免在以后的统计过程中再重复统计。

然后需要统计两个长度为2的子数组之间的逆序对,并合并两个子数组,如图5.3所示。两个子数组分别从末尾开始判断数的大小,如图5.2(a)中指针P1、P2。如果 P1 大于 P2,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数,即 P2 以及之前的所有数字的个数。如果 P1 小于或等于 P2, 则不构成逆序对。每次比较的时候,我们都把较大的数字从后往前复制到一个辅助数组,确保辅助数组中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。

以此类推,使用这个方法去求出其他数组的逆序对个数。在解这一题的时候,还有一点需要注意,除了需要将总输出结果 (count + countL + countR) 对1000000007取模,还要在计算过程中将 count += j + 1 对1000000007取模,以免count超出 Integer.MAX_VALUE。

剑指offer-36:数组中的逆序对的更多相关文章

  1. 剑指 Offer 51. 数组中的逆序对 + 归并排序 + 树状数组

    剑指 Offer 51. 数组中的逆序对 Offer_51 题目描述 方法一:暴力法(双层循环,超时) package com.walegarrett.offer; /** * @Author Wal ...

  2. 【Java】 剑指offer(51)数组中的逆序对

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成 ...

  3. 【剑指offer】数组中的逆序对

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/mmc_maodun/article/details/27520535 转载请注明出处:http:// ...

  4. Go语言实现:【剑指offer】数组中的逆序对

    该题目来源于牛客网<剑指offer>专题. 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对10000 ...

  5. 【剑指offer】数组中的逆序对。C++实现

    原创文章,转载请注明出处! 博客文章索引地址 博客文章中代码的github地址 # 题目 # 思路 基于归并排序的思想统计逆序对:先把数组分割成子数组,再子数组合并的过程中统计逆序对的数目.统计逆序对 ...

  6. 微软面试题:剑指 Offer 51. 数组中的逆序对 Hard 出现次数:3

    题目描述: 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对. 输入一个数组,求出这个数组中的逆序对的总数. 示例 1: 输入: [7,5,6,4] 输出: 5 限制: ...

  7. 剑指Offer 35. 数组中的逆序对 (数组)

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

  8. [剑指Offer] 35.数组中的逆序对

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

  9. 剑指offer:数组中的逆序对

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

  10. 剑指offer——54数组中的逆序对

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

随机推荐

  1. JDK API1.6常用方法

    一.String类 常见用法 : (String类代表字符串,JAVA程序中的所有字符串字面值(如“abc”)都作为此类的实例实现,字符串是常量,他们的值在创建之后不能更改,字符串缓冲区支持可变的字符 ...

  2. mui开发:苹果手机自动全屏解决方案

    前一段时间,使用mui写app时,出现了苹果手机播放视频时,自动全屏的情况,并且无法点击控件,只能等到播放完毕后点击控件. 那么怎么解决这个问题呢,弟弟们请看我的下面. 1.在页面的video标签中, ...

  3. 重新调用 layoutSubview

    重新调用 layoutSubview

  4. 2019-2020-1 20199304《Linux内核原理与分析》第九周作业

    第八章 进程的切换和系统的一般执行过程 知识点 1.进程调度的时机 1.1硬中断和软中断 中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而 ...

  5. Too many open files的四种解决办法【华为云技术分享】

    [摘要] Too many open files有四种可能:一 单个进程打开文件句柄数过多,二 操作系统打开的文件句柄数过多,三 systemd对该进程进行了限制,四 inotify达到上限. 领导见 ...

  6. 即将到来的“分布式云”(DPaaS):分布式计算+ DB +存储即服务

    我在区块链会议上就即将到来的公共"分布式云"系统进行了讨论,该系统将主流的公共云平台(如AWS,Azure,Google Cloud,Heroku等)与区块链和P2P网络相结合,比 ...

  7. 图解leetcode —— 124. 二叉树中的最大路径和

    前言: 每道题附带动态示意图,提供java.python两种语言答案,力求提供leetcode最优解. 描述: 给定一个非空二叉树,返回其最大路径和. 本题中,路径被定义为一条从树中任意节点出发,达到 ...

  8. 浏览器/外网访问docker container中的hadoop

    假设你制作了个docker的 hadoop的镜像,镜像名叫 hd_image,如果想在外网的浏览器中访问hadoop的50070和8088端口,则在启动镜像hd_image时, 脚本如下: docke ...

  9. 有了 serverless,前端也可以快速开发一个 Puppeteer 网页截图服务

    更多云原生技术资讯可关注阿里巴巴云原生技术圈. Puppeteer 是什么? puppeteer 官网的介绍如下: Puppeteer is a Node library which provides ...

  10. javascript类数组

    一.类数组定义: 而对于一个普通的对象来说,如果它的所有property名均为正整数,同时也有相应的length属性,那么虽然该对象并不是由Array构造函数所创建的,它依然呈现出数组的行为,在这种情 ...