一、题目

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

二、思路

方法一:
  看到这个题目,我们的第一反应是顺序扫描整个数组。没扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和O(n)这个数字比较,因此这个算法的时间复杂度为O(n^2)。
 
方法二:
   我们以数组{7,5,6,4}为例来分析统计逆序对的过程。每次扫描到一个数字的时候,我们不拿ta和后面的每一个数字作比较,否则时间复杂度就是O(n^2),因此我们可以考虑先比较两个相邻的数字。
                                                      
 1、在上图(a)和(b)中,我们先把数组分解成两个长度为2的子数组,再把这两个子数组分别拆成两个长度为1的子数组。接下来一边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中7大于5,因此(7,5)组成一个逆序对。同样在第二对长度为1的子数组{6}、{4}中也有逆序对(6,4)。由于我们已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组 排序 ,如上图(c)所示, 以免在以后的统计过程中再重复统计。
 
2、接下来我们统计两个长度为2的子数组之间的逆序对。合并子数组并统计逆序对的过程如下图如下图所示。     
     
我们先用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。如果第一个子数组中的数字大于第二个数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数,如下图(a)和(c)所示。如果第一个数组的数字小于或等于第二个数组中的数字,则不构成逆序对,如图b所示。每一次比较的时候,我们都把较大的数字从后面往前复制到一个辅助数组中,确保 辅助数组(记为copy) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。对应图中的详细过程如下:
  (a) P1指向的数字大于P2指向的数字,表明数组中存在逆序对.P2 指向的数字是第二个子数组的第二个数字, 因此第二个子数组中有两个数字比7 小. 把逆序对数目加2,并把7 复制到辅助数组,向前移动P1和P3. 
  (b) P1指向的数字小子P2 指向的数字,没有逆序对.把P2 指向的数字复制到辅助数组,并向前移动P2 和P3 . 
  (c) P1指向的数字大于P2 指向的数字,因此存在逆序对. 由于P2 指向的数字是第二个子数组的第一个数字,子数组中只有一个数字比5 小. 把逆序对数目加1 ,并把5复制到辅助数组,向前移动P1和P3 .
  (d) 即复制第二个子数组最后剩余的4 到辅助数组中. 
3、经过前面详细的诗论, 我们可以总结出统计逆序对的过程:
  先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。

三、代码

/*归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),
合并数组,合并时,出现前面的数组值array[i]大于后面数组值array[j]时;则前面
数组array[i]~array[mid]都是大于array[j]的,count += mid+1 - i
参考剑指Offer,但是感觉剑指Offer归并过程少了一步拷贝过程。
还有就是测试用例输出结果比较大,对每次返回的count mod(1000000007)求余
*/ public class Solution {
public int InversePairs(int[] array) {
if (array == null || array.length == 0) {
return 0;
} //定义辅助数组
int[] copy = new int[array.length];
for (int i = 0; i < array.length; i++) {
copy[i] = array[i];
} //数值过大求余
int count = InversePairsCore(array, copy, 0, array.length - 1); //返回结果
return count; } private int InversePairsCore(int[] array, int[] copy, int low, int high) {
if (low == high) {
return 0;
}
int mid = (low + high) >> 1;
int leftCount = InversePairsCore(array, copy, low, mid) % 1000000007;
int rightCount = InversePairsCore(array, copy, mid + 1, high) % 1000000007; int count = 0;
int i = mid;
int j = high;
int locCopy = high; while (i >= low && j > mid) {
if (array[i] > array[j]) {
count += j - mid;
copy[locCopy--] = array[i--];
if (count >= 1000000007)//数值过大求余
{
count %= 1000000007;
}
} else {
copy[locCopy--] = array[j--];
}
} for (; i >= low; i--) {
copy[locCopy--] = array[i];
} for (; j > mid; j--) {
copy[locCopy--] = array[j];
} for (int s = low; s <= high; s++) {
array[s] = copy[s];
} return (leftCount + rightCount + count) % 1000000007;
}
}

---------------------------------------------

参考链接:

https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5

剑指offer三十五之数组中的逆序对的更多相关文章

  1. 《剑指offer》面试题51. 数组中的逆序对

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

  2. 剑指offer二十八之数组中出现次数超过一半的数字

    一.题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. ...

  3. 【剑指Offer】数字在排序数组中出现的次数 解题报告(Python)

    [剑指Offer]数字在排序数组中出现的次数 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interv ...

  4. Leetcode - 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)

    剑指offer 面试题29:数组中出现次数超过一半的数字 提交网址: http://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163 ...

  5. [简单-剑指 Offer 53 - I. 在排序数组中查找数字 I]

    [简单-剑指 Offer 53 - I. 在排序数组中查找数字 I] 统计一个数字在排序数组中出现的次数. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8 输出 ...

  6. 剑指 Offer 53 - I. 在排序数组中查找数字 I + 二分法

    剑指 Offer 53 - I. 在排序数组中查找数字 I Offer_53_1 题目描述 方法一:使用HashMap package com.walegarrett.offer; /** * @Au ...

  7. 力扣 - 剑指 Offer 53 - I. 在排序数组中查找数字 I

    题目 剑指 Offer 53 - I. 在排序数组中查找数字 I 思路1 一般来说,首先想到的是使用一个变量,从头开始遍历整个数组,记录target数组出现的次数,但是这样的时间复杂度是O(n),还是 ...

  8. 【Java】 剑指offer(53-1) 数字在排序数组中出现的次数

    正文 本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 统计一个数字在排序数组中出现的次数.例如输入排序数组{1, ...

  9. 剑指offer(一):二维数组中的查找

    说明: 1.本系列是根据<剑指Offer>这个系列做的一个小笔记. 2.直接动力是因为师兄师姐找工作很难,而且机械出生的我面试算法更难. 3.刚开始准备刷LeetCode.LintCode ...

随机推荐

  1. 2018.08.30 NOIP模拟 kfib(矩阵快速幂+exgcd)

    [输入] 一行两个整数 n P [输出] 从小到大输出可能的 k,若不存在,输出 None [样例输入 1] 5 5 [样例输出] 2 [样例解释] f[0] = 2 f[1] = 2 f[2] = ...

  2. 2018.07.25 bzoj3878: [Ahoi2014&Jsoi2014]奇怪的计算器(线段树)

    传送门 线段树综合. 让我想起一道叫做siano" role="presentation" style="position: relative;"&g ...

  3. hdu-1069(dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1069 题意:一群猴子,给出n块砖的长x宽y高z,用这些砖拼起的高度最高是多少, 要求底下的砖的长宽都要 ...

  4. 第六章 副词(Les adverbes )

    副词属于不变词类,无性.数变化(tout除外),它的功能是修饰动词.形容词.副词或句子. ➡副词的构成 ⇨单一副词 bien tard hier mal vite tôt très souvent  ...

  5. sql join用法(转)

    left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录inner join(等值连接) 只 ...

  6. 【转】Paxos算法1-算法形成理论

    ——转自:{老码农的专栏} Paxos算法的难理解与算法的知名度一样令人敬仰,从我个人的经历而言,难理解的原因并不是该算法高深到大家智商不够,而在于Lamport在表达该算法时过于晦涩且缺乏一个完整的 ...

  7. UVa 11039 Building designing (贪心+排序+模拟)

    题意:给定n个非0绝对值不相同的数,让他们排成一列,符号交替但绝对值递增,求最长的序列长度. 析:我个去简单啊,也就是个水题.首先先把他们的绝对值按递增的顺序排序,然后呢,挨着扫一遍,只有符号不同才计 ...

  8. (博弈)Simple Game --codeforces--570B

    链接: http://codeforces.com/problemset/problem/570/B http://acm.hust.edu.cn/vjudge/contest/view.action ...

  9. linux环境下(非UI操作)所有软件的安装与卸载总结

    UI界面的软件管理 linux下的软件一般都是经过压缩的,主要的格式有这几种:rpm.tar.tar.gz.tgz等.所以首先拿到软件后第一件事就是解压缩. 在xwindow下以rpm格式的软件安装比 ...

  10. 为Quartus工程生成rbf文件的方法

    rbf文件是Quartus编译生成的fpga配置文件的二进制数据量格式的文件,主要用于使用外部主机通过PS方式配置FPGA. 在含ARM硬核的SoC FPGA中,可以使用HPS配置FPGA,配置时分为 ...