牛客网剑指offer第35题——数组中的逆序对

题目:

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

刚看到这个题目的时候,我的第一直觉是,不能对数组排序,因为排序打乱了数字的相对顺序,无法求解逆序对。于是采用了遍历的方法,很明显,时间复杂度位O(n2),导致最周直接因为运行时间太长没通过。因此,这道题目中一定存在比较巧妙的方法:而其方法就是:因为归并排序中有一步:将两个有序数组合并成一个有序数组,在这个递归合并的过程中,我们可以求得逆序对。

可以参考下面的网站:https://blog.csdn.net/weixin_43935894/article/details/87628969

具体思路如下:

在合并的过程中是将两个相邻并且有序的序列合并成一个有序序列,如以下两个有序序列

Seq1:3  4  5

Seq2:2  6  8  9

合并成一个有序序:

Seq:2  3  4  5  6  8  9

对于序列seq1中的某个数a[i],序列seq2中的某个数a[j],如果a[i]<a[j],没有逆序数,如果a[i]>a[j],那么逆序数为seq1中a[i]后边元素的个数(包括a[i]),即len1-i+1,

这样累加每次递归过程的逆序数,在完成整个递归过程之后,最后的累加和就是逆序的总数
完整代码如下:

 1 class Solution {
 2 public:
 3      unsigned long long int cnt = 0;
 4      void Sort(vector<int>&data,int l,int mid,int r)//将两个有序数组合并成一个有序数组,这是归并排序的前提
 5      {  
 6         vector<int> tmp(r-l+1);
 7         int i = l;//左边界开始
 8         int j =  mid+1;//右边界开始
 9         int k = 0;
10         while((i<=mid) && (j <= r))
11         {
12             if(data[i]<=data[j])
13                tmp[k++] = data[i++];
14             else
15             {
16                tmp[k++] = data[j++];
17                 cnt+=mid-i+1;
18             }
19         }
20         while((i > mid) && (j <= r))
21         {
22             tmp[k++] = data[j++];
23         }
24         while((j > r) && (i <= mid))
25         {
26             tmp[k++] = data[i++];
27         }
28         for(int i = 0;i <r-l+1;i++)
29            data[l+i]=  tmp[i];        //这一步对于data的索引极其容易出错
30     }
31       void MergeSort(vector<int>& data,int l,int r)
32     {
33         if(l>=r)
34             return;
35            int mid = l+(r-l)/21;
36             MergeSort(data,l,mid);
37             MergeSort(data,mid+1,r);
38             Sort(data,l,mid,r);//
39     }
40     int InversePairs(vector<int> data) {
41         MergeSort(data,0,data.size()-1);
42         return cnt%1000000007;
43     }
44 };

 

posted @ 2020-03-14 20:10  少年π  阅读(207)  评论(0编辑  收藏  举报