题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数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

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

过程总结:

先把数组分隔成子数组,统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。

 //最笨的方法
class Solution01 {
public:
int InversePairs(vector<int> data) {
if (data.size() < )return ;
set<int>s;
s.insert(data[]);
int res = ;
for (int i = ; i < data.size(); ++i)
{
if (data[i] < *(s.begin()))
res += s.size();
else if (data[i] > *(--s.end()))
res += ;
else
{
int k = ;
for (auto ptr = s.begin(); ptr != s.end(); ++ptr, ++k)
{
if (*ptr > data[i])
{
res += s.size() - k;
break;
}
}
}
s.insert(data[i]);
}
return res;
}
}; //书本代码,有点乱
class Solution02 {
public:
int InversePairs(vector<int> data) {
if (data.size() < )return ;
vector<int>v;//用来复制的
v = data;
return InversePairsCore(data, v, , data.size() - );
} int InversePairsCore(vector<int>&data, vector<int>&copy, int start, int end)
{
if (start == end)
{
copy[start] = data[start];
return ;
} int length = (end - start) / ; int left = InversePairsCore(copy, data, start, start + length) % ;
int right = InversePairsCore(copy, data, start + length + , end) % ; // i初始化为前半段最后一个数字的下标
int i = start + length;
// j初始化为后半段最后一个数字的下标
int j = end;
int indexCopy = end;
int count = ;
while (i >= start && j >= start + length + )
{
if (data[i] > data[j])
{
copy[indexCopy--] = data[i--];
count += j - start - length;
if (count >= )//数值过大求余
{
count %= ;
}
}
else
{
copy[indexCopy--] = data[j--];
}
} for (; i >= start; --i)
copy[indexCopy--] = data[i]; for (; j >= start + length + ; --j)
copy[indexCopy--] = data[j]; return (left + right + count) % ;
}
}; //使用归并排序思想
class Solution03 {
private:
int count = ;
public:
int InversePairs(vector<int> data) {
if (data.size() < )return ;
mergeSort(data, , data.size() - );
return count;
}
void mergeSort(vector<int>&data, int L, int R)
{
if (L < R)
{
int M = (L + R) / ;
mergeSort(data, L, M);
mergeSort(data, M + , R);
merge(data, L, M, R);
}
}
void merge(vector<int>&data, int L, int M, int R)
{
vector<int>temp(R - L + );
int t = R - L;
int tL = M;
int tR = R;
while (tL >= L && tR >= M + )
{
if (data[tL] > data[tR])
{
count += tR - M;
temp[t--] = data[tL--];
count %= ;
}
else
temp[t--] = data[tR--];
}
while (tL >= L)
temp[t--] = data[tL--];
while (tR >= M + )
temp[t--] = data[tR--];
for (int i = ; i <= R - L; ++i)
data[L + i] = temp[i];
}
};

剑指offer——54数组中的逆序对的更多相关文章

  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 35. 数组中的逆序对 (数组)

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

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

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

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

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

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

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

随机推荐

  1. MyBatis原理,Spring、SpringBoot整合MyBatis

    1. MyBatis概述 MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可 ...

  2. redis基本介绍搭建篇

    1.redis基本介绍 1.1 redis介绍 Redis属于非关系型数据库和Memcached类似,redis也是一个key-value型存储系统. 但redis支持的存储value类型相对更多,包 ...

  3. 点读系列《Jenkins用户文档》

    Jenkins用户手册官网地址:点击打开 开源 CI&CD 软件 自动化各种任务, build test deploy 支持各种运行方式 Jenkins入门 入门指南 需要java和docke ...

  4. CF1239

    然后ZUTTER_打的第一场div1以没敢交题 完!美!结!束!!! A 没有发现性质就找规律海星 我们可以算出一列的贡献:\(g[i][0]\)表示上两个不同,\(g[i][1]\)表示上两个相同就 ...

  5. ionic2(3) 密码键盘组件 ionic2-pincode-input 使用

    1.效果展示: 2.安装: npm install ionic2-pincode-input --save 3.app.module.ts配置 app.module.ts import { NgMod ...

  6. python字典拼接方法

    python的dict拼接有多种方法,其中一种很好用而且速度非常快: x = {**a, **b} 效果等价于: x = a.copy() x.update(b) 注意update()是没有返回值的 ...

  7. 配置基于python的VIM环境

    配置基于python的VIM环境 安装插件管理工具 为防止过多插件管理的麻烦,首先安装vim的插件管理工具Vundle.vundle本身的github软件已经有相关的中文文档,地址如下: vundle ...

  8. 使用ReadStream方法读取文件事件传递过程

    const fs = require('fs'); let file = fs.createReadStream("filename.js"); file.on("ope ...

  9. element-ui中table渲染的快速用法

    element-ui中对table数据的渲染有一些模板式的操作,基本按照模板渲染数据即可 基本模板样式如下 <el-table :data="studentData.rows" ...

  10. Java优化性能

    尽量在合适的场合使用单例使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:第一,控制资源的使用,通过线程同步来控制资源的并 ...