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

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数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) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。

过程总结:

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

  1 //最笨的方法
  2 class Solution01 {
  3 public:
  4     int InversePairs(vector<int> data) {
  5         if (data.size() < 2)return 0;
  6         set<int>s;
  7         s.insert(data[0]);
  8         int res = 0;
  9         for (int i = 1; i < data.size(); ++i)
 10         {
 11             if (data[i] < *(s.begin()))
 12                 res += s.size();
 13             else if (data[i] > *(--s.end()))
 14                 res += 0;
 15             else
 16             {
 17                 int k = 0;
 18                 for (auto ptr = s.begin(); ptr != s.end(); ++ptr, ++k)
 19                 {
 20                     if (*ptr > data[i])
 21                     {
 22                         res += s.size() - k;
 23                         break;
 24                     }
 25                 }
 26             }
 27             s.insert(data[i]);
 28         }
 29         return res;
 30     }
 31 };
 32
 33 //书本代码,有点乱
 34 class Solution02 {
 35 public:
 36     int InversePairs(vector<int> data) {
 37         if (data.size() < 2)return 0;
 38         vector<int>v;//用来复制的
 39         v = data;
 40         return InversePairsCore(data, v, 0, data.size() - 1);
 41     }
 42
 43     int InversePairsCore(vector<int>&data, vector<int>&copy, int start, int end)
 44     {
 45         if (start == end)
 46         {
 47             copy[start] = data[start];
 48             return 0;
 49         }
 50
 51         int length = (end - start) / 2;
 52
 53         int left = InversePairsCore(copy, data, start, start + length) % 1000000007;
 54         int right = InversePairsCore(copy, data, start + length + 1, end) % 1000000007;
 55
 56         // i初始化为前半段最后一个数字的下标
 57         int i = start + length;
 58         // j初始化为后半段最后一个数字的下标
 59         int j = end;
 60         int indexCopy = end;
 61         int count = 0;
 62         while (i >= start && j >= start + length + 1)
 63         {
 64             if (data[i] > data[j])
 65             {
 66                 copy[indexCopy--] = data[i--];
 67                 count += j - start - length;
 68                 if (count >= 1000000007)//数值过大求余
 69                 {
 70                     count %= 1000000007;
 71                 }
 72             }
 73             else
 74             {
 75                 copy[indexCopy--] = data[j--];
 76             }
 77         }
 78
 79         for (; i >= start; --i)
 80             copy[indexCopy--] = data[i];
 81
 82         for (; j >= start + length + 1; --j)
 83             copy[indexCopy--] = data[j];
 84
 85         return (left + right + count) % 1000000007;
 86     }
 87 };
 88
 89
 90 //使用归并排序思想
 91 class Solution03 {
 92 private:
 93     int count = 0;
 94 public:
 95     int InversePairs(vector<int> data) {
 96         if (data.size() < 2)return 0;
 97         mergeSort(data, 0, data.size() - 1);
 98         return count;
 99     }
100     void mergeSort(vector<int>&data, int L, int R)
101     {
102         if (L < R)
103         {
104             int M = (L + R) / 2;
105             mergeSort(data, L, M);
106             mergeSort(data, M + 1, R);
107             merge(data, L, M, R);
108         }
109     }
110     void merge(vector<int>&data, int L, int M, int R)
111     {
112         vector<int>temp(R - L + 1);
113         int t = R - L;
114         int tL = M;
115         int tR = R;
116         while (tL >= L && tR >= M + 1)
117         {
118             if (data[tL] > data[tR])
119             {
120                 count += tR - M;
121                 temp[t--] = data[tL--];
122                 count %= 1000000007;
123             }
124             else
125                 temp[t--] = data[tR--];
126         }
127         while (tL >= L)
128             temp[t--] = data[tL--];
129         while (tR >= M + 1)
130             temp[t--] = data[tR--];
131         for (int i = 0; i <= R - L; ++i)
132             data[L + i] = temp[i];
133     }
134 };

原文地址:https://www.cnblogs.com/zzw1024/p/11701321.html

时间: 2024-11-05 05:37:29

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

剑指offer (36) 数组中的逆序对

题目:在数组中的两个数字如果前面一个数字大于后面一个数字,则这两个数字组成一个逆序对 题解分析: 首先应该想到很简单的一种解法,顺序遍历数组,对每个数,逐个比较该数字和其以后的数字,T(n) = O(n^2) (1)总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量 count += Merge(data, temp, first, mid);//找左半段的逆序对数目 count += Merge(data, temp, mid + 1, e

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

# @left part: [start, mid] # @right part: (mid, end] def merge(data, start, mid, end): if mid < start or end < mid: return 0 reverse = 0 ''' @ for start, it play as the start index of left part, and mid @ play as the end index of left part; @ mid +

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

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 思路 如果遍历数组,对每个数字都和后面的数字比较大小,时间复杂度为O(n^2),效率太低. 利用归并排序的思想,先将数组分解成为n个长度为1的子数组,然后进行两两合并同时排好顺序. 在对两个子区域合并排序时,记左边区域(下标为start~mid)的指针

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

题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 [思路]看到这样的题目,最简单的想法就是遍历每一个元素,让其与后面的元素对比,如果大于则count++,但是这样的时间复杂度是o(n2),根据题目给出的数据量,很明显超时.因此想到了用归并排序的思想. 1 class Solution { 2 public: 3 void MergeC

剑指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 # -*- coding: utf-8 -*

剑指offer[35]——数组中的逆序对

题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数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 这道题目就是一个归并排序的思路,归并

剑指offer: 数组中的逆序对

1. 最简单的思路,对每个值,遍历与其逆序的数组对:但时间复杂度太高: 2. 归并排序的思路: 先将数组分隔成子数组,先统计出子数组内的逆序对的数目,然后统计两个相邻子数组之间的逆序对的数目: int InversePairsCore(int *data, int * copy, int start, int end) { //递归介绍条件,只剩一个元素 if(start==end) { copy[start]=data[start]; return 0; } int length=(end-s

剑指offer35:数组中的逆序对

1 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007 2 思路和方法 利用归并排序的思想,先把数组分隔成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目.注意在合并两个已排序的子数组后,要更新数组.O(n*log(n)). 3 C++核心代码 1 class Solution { 2 public

[剑指offer]51-数组中的逆序对(归并排序)

题目链接 https://www.nowcoder.com/questionTerminal/96bd6684e04a44eb80e6a68efc0ec6c5 题意 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即输出P%1000000007.题目保证输入的数组中没有的相同的数字. 解题思路 在归并排序的过程中计逆序对.时间复杂度O(nlogn),空间复杂度O(n). 将