数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
例如:
例如,有一个数组为Array[0..n] 其中有元素a[i],a[j].如果 当i< j时,a[i]>a[j],那么我们就称(a[i],a[j])为一个逆序对。在数组{7,5,6,4}中一共存在5对逆序对,分别是(7,6),(7,5),(7,4),(6,4),(5,4)。
解题思路
看到这样的题目,最简单的想法就是遍历每一个元素,让其与后面的元素对比,如果大于则count++
,但是这样的时间复杂度是o(n2)
。这题有更好的解决方法,时间复杂度只需要o(nlogn)
。其实这道题目的思路跟归并排序差不多,求逆序对的过程就是一个求归并排序的过程,在求出逆序对以后,原数组变得有序,是通过归并排序得到的。
总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量
inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序对数目
inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序对数目
然后求段间的逆序对数量,如下面的代码
inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序对以后两段数组有序,然后找两段之间的逆序对。最小的逆序段只有一个元素。
然后在求段间逆序对的时候,我们分为arry[start…mid]和arry[mid+1…end],然后设置两个指针ij分别指向两段数组的末尾元素,也就是i=mid,j=end。然后比较arry[i]和arry[j]:
- 如果arry[i]>arry[j],因为两段数组都是有序的,所以arry[i]>arry[mid+1…j],这些都是逆序对,我们统计出的逆序对为j-(mid+1)+1=j-mid。并且将大数arry[i]放入临时数组temp[]当中,i往前移动;
- 如果arry[i]< arry[j],则将大数arry[j]放入temp[]中,j往前移。
仿造归并排序算法:
package com.genge.offer;
/**
* Created by Genge on 2016-06-30.
*/
public class InversePairs {
public int InversePairs(int [] array) {
int[] temp = new int[array.length];
return InversePairsCore(array, 0, array.length - 1, temp);
}
//分治
private int InversePairsCore(int[] array, int start, int end, int[] temp) {
int count = 0;
if (start < end) {
int mid = (start + end) / 2;
count += InversePairsCore(array, start, mid, temp); //前半段逆序数
count += InversePairsCore(array, mid + 1, end, temp); //后半段逆序数
count += MergeArray(array, start, mid, end, temp); //归并后整段逆序数
}
return count;
}
//合并
// 将有二个有序数列a[first...mid]和a[mid...last]合并。
private int MergeArray(int[] array,int start,int mid,int end,int temp[]){
int i = start, j = mid + 1;
int m = mid, n = end;
int k = 0, count = 0;
while (i <= m && j <= n) {
if (array[i] > array[j]) {
temp[k++] = array[j++];
// 【因为数组此时是有序数组】
// 因为如果a[i]此时比右数组的当前元素a[j]大,
// 那么左数组中a[i]后面的元素就都比a[j]大
count += mid - i + 1;
}else{
temp[k++] = array[i++];
}
}
while(i <= m ){
temp[k++] = array[i++];
}
while (j <= n) {
temp[k++] = array[j++];
}
System.arraycopy(temp, 0, array, start, k);//排序结果复制到源数组,用for循环也行
return count;
}
}