数组中的逆序对
作者:互联网
数组中的逆序对
基本思想
拿到这个题目,最简单粗暴的方法,就是用两个for循环,一 一比较统计出逆序对,时间复杂度为O(n^2),当数组元素较多时,很有可能会超时。
那我们能想到什么方法呢?
给定一个特殊数组data={5,7,4,6}(前半段和后半段分别有序);,计算它的逆序对:
对于元素5:0个。f(0)
对于元素7:0个。f(1)
对于元素4:1个。f(2)
对于元素6:2个。f(3)
所以整个数组的逆序对cnt=0+0+1+2=3个。
我们试着对data数组进行归并排序:
data前半段数组:{5,7}
data后半段数组:{4,6}
归并过程:如图
- j 指向 后半段的4,i 指向前半段的5时,4<5。说明前半段中的 5 以及5后面的数与 4 均构成逆序对,即前半段中剩下的没来得及复制到临时数组的所有数均与 4构成逆序对,统计此时做半段没来得及复制到临时数组temp的剩余的元素数量:2。j++,判断下一个
- j 指向 后半段的6,i 指向前半段的5时,6>5。5 复制到临时数组。i++。左半段的 5与前面的数并不构成逆序对。(左半段升序,不构成逆序对)
- j 指向 后半段的6,i 指向前半段的 7 时,6<7。说明前半段中的 7 以及7后面的数与 6 均构成逆序对,即前半段中剩下的没来得及复制到临时数组的所有数均与 6构成逆序对,统计此时做半段没来得及复制到临时数组temp的剩余的元素数量:1。j++,判断下一个
- j 已经到末尾了,将左半段剩余的数全部复制到temp中(左半段升序,不构成逆序对)
我们发现,在归并排序时,我们可以顺便完成每个元素能构成的逆序对的统计 f(j)。由于合并是从小到大的,当右边的data[j]复制到temp中时,左边没来得及复制到temp的那些数都比data[j]大,此时只要在累加器中加上左边的剩余元素数量mid-i即可。
其实,我当时在想,这样统计出来的逆序对会不会漏或者多统计。实际上不会,在归并排序过程中,自底向上排序。每次在合并的时候,计算每个元素能构成的逆序对的数量。递归求解统计了 i 和 j 都在左边或右边的逆序对个数,“合并问题”则是统计 i 在左边,但是 j 在右边的逆序对个数。
代码
class Solution {
public:
int InversePairs(vector<int> data) {
cnt=0;
vector<int> temp(data.size());
merge_sort(data,0,data.size(),temp);
return cnt;
}
//在归并排序中,计算统计逆序对的数量 左闭右开
void merge_sort(vector<int>& data,int start,int end,vector<int>& temp){
if(end-start<=1) return;//区间中只有一个元素,一个元素认为有序,且逆序对为0
int mid=start+(end-start)/2;//避免start+end过大溢出
//cout<<mid;
merge_sort(data,start,mid,temp);//归并排序左半部分
merge_sort(data,mid,end,temp); //归并排序右半部分
//左右不分均排序后,合并到临时数组temp中
int i=start,j=mid,index=start;//i指向左半边有序数组,j指向右半边有序数组,index指向temp临时数组
while(i<mid||j<end){
//右半边数组已经放完, //或左半边数组i位置的数比右半边数组j元素要小 .这两种情况均是把左半边的数组复制到临时数组temp
if(j>=end||(i<mid&&data[i]<=data[j])) temp[index++]=data[i++];
else {
temp[index++]=data[j++];//右半边j位置元素比左半边i小,右半边复制到临时数组
//一旦是在将右半边数组复制到临时数组时,此时左半边剩余的还没来得及复制到temp的数均比j位置的数大,它们与j位置的数均构成逆序对
//因此,在从右半边复制j到temp时,统计左半边还剩多少元素k,这k个数与j位置元素构成逆序对
cnt+=mid-i;
cnt%=1000000007;
}
}
//将有序的temp复制回data
i=start;
while(i<end) {//data[i] =temp[i++];
data[i] =temp[i];
i++;
}
}
private:
int cnt;
};
总结
没有马上想到用归并排序来做。需要对一些经典的基础算法加深理解,其实很多题目都是在一些经典的算法中添加某些操作,来解决问题,向这个就是利用归并排序求逆序对,还有之前的利用堆排序求top k的问题等等。如果对归并排序、堆排序等经典算法有深层次的理解,就能够将它们应用到相应的实际问题中。编程路上,砥砺前行~
标签:复制到,半边,temp,数组,data,逆序 来源: https://blog.csdn.net/qq_42211773/article/details/115590473