题目描述:链接:https://www.nowcoder.com/questionTerminal/bb06495cc0154e90bbb18911fd581df6
来源:牛客网
有一组数,对于其中任意两个数组,若前面一个大于后面一个数字,则这两个数字组成一个逆序对。请设计一个高效的算法,计算给定数组中的逆序对个数。
给定一个int数组A和它的大小n,请返回A中的逆序对个数。保证n小于等于5000。
在看到本题目之后的第一反应就是暴力法,通过两个嵌套循环依次比较,但是时间复杂度比较大O(n^2)
public int count(int[] A,int n){
int count=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(A[i]>A[j]){
count++;
}
}
}
return count;
}
再看了其他人的答案之后,觉得很巧妙,利用归并思想
解题思路:分治思想,采用归并排序的思路来处理,先分后治:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目,在统计逆序对的过程中,还需要对数组进行排序,其实这个排序过程就是归并排序的思路,逆序对的总数=左边数组中的逆序对的数量+右边数组中逆序对的数量+左右结合成新数组时出现的逆序对的数量。
private int count(int[] a,int n){
if(a==null||a.length==0){
return -1;
}
return mergeSort(a,0,n-1);
}
private int mergeSort(int[] a, int left, int right) {
if(left==right){
return 0;
}
int mid=(left+right)/2;
return mergeSort(a,left,mid)+mergeSort(a,mid+1,right)+merge(a,left,mid,right);
}
private int merge(int[] a, int left, int mid, int right) {
int[] temp=new int[right-left+1];
int index=0;
int i=left;
int j=mid+1;
int inverNum=0;
while(i<=mid&&j<=right){
if(a[i]<a[j]){
temp[index++]=a[i++];
}
else{
inverNum+=mid-i+1;
temp[index++]=a[j++];
}
}
while(i<=mid){
temp[index++]=a[i++];
}
while(j<=right){
temp[index++]=a[j++];
}
for(int k=0;k<temp.length;k++){
a[left++]=temp[k];
}
return inverNum;
}