35.数组中的逆序对(涉及归并排序)
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
第一就是暴力求解法,时间复杂度为o(n^2),空间复杂度o(1)
第二种思路就是使用归并排序的思想进行处理,时间复杂度o(nlog(n)),空间复杂度0(n)*/
可以参考以下这位作者对归并排序的讲解,来理解归并排序。更容易:https://blog.csdn.net/k_koris/article/details/80508543
思路:
1.先用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。
2.如果第一个子数组中的数字大于第二个数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数。
3.如果第一个数组的数字小于或等于第二个数组中的数字,则不构成逆序对。每一次比较的时候,我们都把较大的数字从后面往前复制到一个辅助数组中,确保 辅助数组(记为copy) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。
public class Solution {
int count=0;
public int InversePairs(int [] array) {
if(array.length<=0||array==null){
return 0;
}
//初始化该数组,该数组作为存放临时排序的结果,最后要将排序的结果复制到原数组中
int[] copy=new int[array.length];
for(int i=0;i<array.length;i++){
copy[i]=array[i];
}
int Num=divideMerge(array,copy,0,array.length-1);
//delete[] copy;//删除临时数组
return Num;
}
private int divideMerge(int[] arr,int[] copy,int start,int end){
//递归的终止条件
if(start==end){
copy[start]=arr[start];
return 0;
}
//计算中间值,注意溢出
int mid=(end+start)/2;
//先递归分别计算左右部分逆序对
int leftNum=divideMerge(arr,copy,start,mid)%1000000007;
int rightNum=divideMerge(arr,copy,mid+1,end)%1000000007;
//逆序对计算
int i=mid; //last_in_left
int j=end; //last_in_right
int index=end;// 辅助数组的下标
int count=0;
//再统计出两个相邻子数组之间的逆序对的数目
while((start<=i)&& (mid+1<=j)){
if(arr[i]>arr[j]){
count+=j-mid;//如果左边的最后一个小于右边的最后一个,那么
count%=1000000007;
copy[index--]=arr[i--];//把大的存进去并放在辅助数组的最后一个,并且移动大数数所在的数组的指针既可。保证辅助素组中的数字是递增排序的。
}else{
//若前面小于后面,那么将大的数存进去,并且移动大数数所在的数组的指针既可
copy[index--]=arr[j--];
}
}
//添加剩下的前半部分到拷贝数组中
for(;i>=start;i--)
copy[index--]=arr[i];
//while(start<=i)
// copy[index--]=arr[i--];
//添加剩下的后半部分到拷贝数组中
for(;j>=mid+1;j--)
copy[index--]=arr[j];
//while(mid+1<=j)
// copy[index--]=arr[j--];
//将排好序的copy数组复制到arr中
for(int k=start;k<=end;k++)
arr[k]=copy[k];
//int k=0;
//while(start<=end)
// arr[start++]=copy[k++];
return (count+leftNum+rightNum)%1000000007;
}
}
1.先把数组分割成子数组
2.在统计出子数组内部的逆序对的数目
3.然后统计出相邻子数组之间的逆序对数目
4.在统计逆序对的过程中,需要对数组进行排序,因此增加了一个辅助数组。
5.最后将排好序的数组返回给arr数组。即合并数组。