题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数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
思路
利用归并排序(需要一个归并排序函数和一个合并函数)
以数组7,5,6,4为例
(a) 把长度为4的数组分解成两个长度为2的子数组;
(b) 把长度为2的数组分解成两个成都为1的子数组;
(c ) 把长度为1的子数组 合并、排序并统计逆序对 ;
(d) 把长度为2的子数组合并、排序,并统计逆序对;
上图是把长度为2的子数组合并、排序的具体实现过程:
(a)p1>p2,说明存在逆序对,逆序对为:(5,4)和(7,4),所以逆序对个数是mid+1-p1
再把p2放进辅助数组,移动p2和p
(b)p1<p2,说明不存在逆序对,把p1放进辅助数组,移动p1和p
(c )p1>p2,说明存在逆序对,逆序对为:(7,6),再把p2放进辅助数组,移动p2和p
最终第一个序列还未检测完,所以把第一个序列剩下的元素直接放进辅助数组,即得辅助数组为{4,5,6,7}
实现
public class Solution {
int count; //统计逆序对的数目:写在外层是因为下面多个函数会用到
public int InversePairs(int [] array) {
//归并排序
count=0;
if(array!=null){
MergeSort(array,0,array.length-1);
}
return count;
}
//归并排序---将数组分成多个小数组,本题使用二路归并,递归实现两个数组的排序
public void MergeSort(int[] a,int start,int end){
//递归出口
if(start>=end){
return;
}
int mid=(start+end)>>1;
MergeSort(a,start,mid);
MergeSort(a,mid+1,end);
//最终再合并成一个数组
Merge(a,start,mid,end);
}
//合并:将一个数组中的两个相邻有序区间合并成一个
public void Merge(int[] a,int start,int mid,int end){
//1.定义一个辅助数组装合并后的元素
int[] tmp=new int[end-start+1];
//2.定义三个指针:两个分别指向子数组的开始元素,一个指向辅助数组的开始元素
int p1=start, p2=mid+1, p=0;
//3.比较p1,p2指向元素的大小,移动指针,直到某一个序列遍历完!
while(p1<=mid && p2<=end){
if(a[p1]<=a[p2]){ //前面比后面小:说明没有逆序对,把p1装进辅助数组,并后移p1和p
tmp[p++]=a[p1++];
}
else{ //前面比后面大:说明有逆序对,把p2装进辅助数组,并后移p2和p
tmp[p++]=a[p2++];
count=(count+mid+1-p1)%1000000007; //逆序对个数等于第二个数组开始下标-p1
}
}
//4.如果某个序列未检测完,直接将后面所有元素加到合并的序列中
while(p1<=mid){
tmp[p++]=a[p1++];
}
while(p2<=end){
tmp[p++]=a[p2++];
}
//5.最终把辅助数组里的元素再赋回a[]
for(p=0;p<tmp.length;p++){
a[start+p]=tmp[p];
}
}
}