题目描述:

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

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

示例 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. 源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS)

    简介 AQS 全称是 AbstractQueuedSynchronizer,位于java.util.concurrent.locks 包下面,AQS 提供了一个基于FIFO的队列和维护了一个状态sta ...

  2. 某C++神作,就100句话而已

    假设p是指针,当delete p;时,后面一定要p=NULL将p指向空 cin cout cerr 都是iostream类型的对象.cout<<"hello world" ...

  3. C++的四种强制转型形式:

    C++同时提供了四种新的强制转型形式(通常称为新风格的或C++风格的强制转型): const_cast(expression)dynamic_cast(expression)reinterpret_c ...

  4. 224、Basic Calculator

    Implement a basic calculator to evaluate a simple expression string. The expression string may conta ...

  5. 服务器虚拟化 - PVE

    服务器虚拟化 - Hypervisor 服务器虚拟化软件,也叫 Hypervisor--虚拟机管理程序,有时也称做 Virtual Machine Monitor(VMM),它可以在宿主机上创建并管理 ...

  6. Flink处理函数实战之三:KeyedProcessFunction类

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. RTSP服务端开发概述

    一 概述 RTSP(Real Time Streaming Protocol),RFC2326,实时音视频流传输协议,是TCP/IP协议体系中的一个应用层协议.该协议定义了一对多应用程序如何有效地通过 ...

  8. 微信公众号获取openid(php实例)

    微信公众号获取openid 公众号获取openid的方法跟小程序获取openid其实是一样的,只是code获取的方式不一样 小程序获取code: 用户授权登录时调用wx.login即可获取到code ...

  9. div可以滚动但不显示滚动条

    首先有3个div, 第1个,固定大小是200*200(单位为px,下同) 第2个,不固定大小,其大小要用第3个div把个撑开,但是这个div必需要有滚动条, 第3个,固定大小与第1个div保持一致20 ...

  10. Camtasia中对录制视频进行编辑——旁白

    相信很多人都遇见过想要录制视频,但是不知道在电脑上用哪一款软件比较好,害怕自己录的视频导出来之后会有水印,或者在录制的过程中遇到麻烦,更或者下载一款带有病毒的软件.那么今天我便给大家推荐一款专业录制屏 ...