题目描述:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。

输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

限制:

0 <= 数组长度 <= 50000

分析:

本题的暴力方法显然容易想到,但是会报超时,难度等级 hard 显示 考察的是使用

时间O(log n*logn)空间 O(n)的解法。可以使用 「归并排序」 和 「线段树」 两种方法。

利用「归并排序」和「线段树」计算逆序对都是非常经典的做法。这里我们暂且只考虑

利用「归并排序」计算逆序对。

思想是「分治算法」,所有的「逆序对」来源于 3 个部分:

  • 左边区间的逆序对;
  • 右边区间的逆序对;
  • 横跨两个区间的逆序对。

计算左边区间的逆序对和右边区间的逆序对都是规模更小的子问题,直接交给递归去完成。

重点是分析计算横跨两个区间的逆序对。计算横跨两个区间的逆序对时,上面的两个子问题都已经让递归函数完成了,

此时,左边区间的逆序对和右边区间的逆序对都已经计算出来了,且左边区间和右边区间都已经有序了。

计算横跨两个区间的逆序对 具体步骤如下:

1.  将给定区间 nums[l,r] 分成 左区间  nums[l , mid] ,右区间 nums[mid + 1,r] ,使用双指针同步遍历左右区间;

2.  如果左边区间当前的元素num[i] 小于等于 右边区间当前的元素nums [j],因为nums[i] 小于等于右边区间

所有的元素nums[j,r],nums[i]  不会和右区间内的元素nums[j,r] 构成逆序对。直接将nums[i] 放入归并排序的辅助空间。

3.   如果左边区间当前的元素num[i] 大于 右边区间当前的元素nums [j],那左边区间元素num[i,mid] ,一共 mid - i + 1 个都比

右边区间当前的元素nums[j]大,且都在右边区间当前的元素前面,和右边区间当前元素 构成  mid - i + 1  个逆序对。

加到总的逆序对数上,再将右边区间当前的元素nums [j] 放到缓冲区。

3.  将左(右)区间比较多出来的元素直接加到辅助空间中.

4. 将辅助空间中排序好的元素 再重新放回nums[l,r];

5. 函数返回当前区间计算得到的总的逆序对数。

代码如下:

 1 class Solution {
2 public:
3 vector<int> tmp;//归并排序的辅助数组
4
5 int reversePairs(vector<int>& nums) {
6 tmp.assign(nums.size(),0);
7 return merge_sort(nums,0,nums.size() - 1);
8 }
9
10 long long int merge_sort(vector<int>& nums,int l,int r)
11 {
12 if(l >= r)//归并排序递归出口
13 {
14 return 0;
15 }
16 int mid = l + (r - l)/2;//将区间一分为二
17 long long ans = merge_sort(nums,l,mid) + merge_sort(nums,mid + 1,r);//递归地求左右子数组的逆序对个数和
18 int k = 0,i = l,j = mid + 1;//计算 横跨左右区间的逆序对
19 while(i <= mid && j <= r)
20 {
21 if(nums[i] <= nums[j])
22 {
23 tmp[k++] = nums[i++];
24 }
25 else
26 { //执行上述递归之后,左右子数组都已经排好序
27 //nums[i:mid] 都比nums[j] 大,都和num[j] 构成逆序对
28 ans += (mid - i + 1);
29 tmp[k++] = nums[j++];
30 }
31 }
32 while(i <= mid) tmp[k++] = nums[i++];
33 while(j <= r) tmp[k++] = nums[j++];
34 //将排序好的元素移回原数组,放在原来的区间上
35 for(i = l,j = 0;i <= r; )
36 {
37 nums[i++] = tmp[j++];
38 }
39 return ans;
40 }
41 };

微软面试题:剑指 Offer 51. 数组中的逆序对 Hard 出现次数:3的更多相关文章

  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——54数组中的逆序对

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

随机推荐

  1. linux 内核 同步原理

    中断分为同步中断和异步中断. 同步中断是由CPU控制单元产生的,"同步"是指只有在一条指令执行完毕后,CPU才会发出中断,比如系统调用 异步中断是由其他硬件设备依照CPU时钟信号产 ...

  2. Socket connect 等简要分析

    connect 系统调用 分析 #include <sys/types.h> /* See NOTES */#include <sys/socket.h>int connect ...

  3. shell编程之字符串操作

    shell中字符串操作主要有以下几种,其中:pattern ,old中可以使用通配符: ${#var} :返回字符串变量var的长度 ${var:m} :返回${var}中从第m+1个字符到最后的部分 ...

  4. 卸载python安装的软件

    python源码安装的软件是无法通过命令卸载的,这个可以通过记录安装过程的形式来卸载安装的软件 以 python2.7.2 为例,在这个目录中有一个 setup.py 的文件,很显然这是安装程序,还是 ...

  5. Qt For Android 环境搭建

    由于时间有限,未抽出时间进行android环境搭建整理的流程,这里先贴出几个参考的地址 https://blog.csdn.net/foruok/article/details/23528293 此链 ...

  6. a标签禁用

    a标签禁用可以使用CSS3的特性来控制 <a class="disabled">我是a标签</a> a.disabled { pointer-events: ...

  7. 使用XSL解析XML输出HTML(XSL学习笔记一)

    最近项目用到 XSL + XML,XML大家应该很熟悉,XSL暂且不解释,先看效果,如果想学习XSL的内容,可以先访问: https://www.w3school.com.cn/xsl/xsl_lan ...

  8. sublime 3 phpfmt配置(大括号对齐)

    默认选项:  default: phpfmt.sublime-settings:         {         "version": 2,         "php ...

  9. CorelDRAW中的合并和群组功能

    在设计过程中,对象就是我们进行操作的主体,这个操作主体可以是单个对象,也可以是多个对象,在有多个对象要进行处理时,就需要用到类似CorelDRAW里的合并和群组这两个功能了. 一.合并与拆分 合并功能 ...

  10. MathType单边大括号的编辑技巧你知道吗?

    大家都知道,一般情况下,数学里面的括号都是成对出现的,但是也有些情况下可以只用到单边的括号,就比如分段函数,在编写的时候只需用到左半边的括号.MathType作为专业的公式编辑器,用它来编写公式再方便 ...