牛客网剑指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 };
stay foolish,stay hungry