1、问题来源
《算法导论》P41思考题2-4:Let A[1..n] be an array of n distinct numbers. (i, j) is called an inversion of A if i < j and A[i] > A[j].
2、解决方案
方案1:不排序的双重循环(时间复杂度O(n^2))
public static int getInversions(int[] array) {
int counter=0;
for (int i = 0; i < array.length; i++) {
for (int j = i+1; j < array.length; j++) {
if (array[i]>array[j]) {
counter++;
}
}
}
return counter;
}
方案2:归并排序过程中计算逆序对(《算法导论》提示的方法,复杂度O(nlgn))
以下过程图解请参照《算法导论》P35页,注意对比本博客中【算法导论学习-002】归并排序(MergeSort)的异同。
<span style="font-size:18px;">/**
* 创建时间:2014年8月10日 下午4:12:19
* 项目名称:Test
* @author Cao Yanfeng
* @since JDK 1.6.0_21
* 类说明:
*/
public class CountInversionsTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array={5,2,4,7,1,3,2,6};
// int result=getInversions(array);
int result=mergeSort(array, 0, array.length-1);
System.out.println(result);
}
public static int mergeSort(int[] arr, int start, int end) {
if (start < end) {
int middle = (start + end) >>> 1;
</span><span style="font-size:18px;color:#cc0000;"><strong> int l=mergeSort(arr, start, middle);
int r=mergeSort(arr, middle + 1, end);
int m=mymerge(arr, start, middle, end);
return l+r+m;
}else {
return 0;</strong></span><span style="font-size:18px;">
}
}
/*
* 归并排序的思路:首先将arr分成两部分,用leftArray和rightArray暂存,然后从头开始比较leftArray和rightArray,
* 谁小谁放入arr,相应指针移动
* 小技巧:leftArray和rightArray都增多最后一位,存放Integer.MAX_VALUE,用于不让它移动到最后
*/
public static int mymerge(int[] arr, int start, int middle, int end) {
/*拆分数组为leftArray和rightArray*/
int leftArrayLength = middle - start + 1;
int rightArrayLength = end - middle;
int[] leftArray = new int[leftArrayLength + 1];//多一位
int[] rightArray = new int[rightArrayLength + 1];//多一位
for (int i = 0; i < leftArrayLength; i++) {
leftArray[i] = arr[start + i];
}
for (int i = 0; i < rightArrayLength; i++) {
rightArray[i] = arr[middle + 1 + i];
}
/*多的最后一位记得要赋最大值*/
rightArray[rightArrayLength] = Integer.MAX_VALUE;
leftArray[leftArrayLength] = Integer.MAX_VALUE;
/*算法的关键部分,左右指针移动*/
int j = 0;
int k = 0;
</span><strong style="font-size: 16px;"><span style="color:#993300;"> int number=0;</span></strong><span style="font-size:18px;">
for (int i = start; i <= end; i++) {
if (leftArray[j] </span><span style="color:#cc0000;font-size: 16px;"><strong><=</strong></span><span style="font-size:18px;"> rightArray[k]) {
arr[i] = leftArray[j];
j++;
} else {
arr[i] = rightArray[k];
k++;
</span><span style="color:#990000;font-size: 16px;"><strong> number+=leftArrayLength-j;</strong></span><span style="font-size:18px;">
}
}
return number;
}
}</span>
方案3:制作数组排序后的副本,利用原数组每个元素在副本中进行二分搜索(复杂度O(nlgn))
例如: int[] array={5,2,4,7,1,3,2,6};
制作排序后的副本 int[] temp={1,2,2,3,4,5,6}
array[0]=5,在temp中二分搜索,得到下表index=5,比它小的有inversions=index-0=5个
temp去除5,int[] temp={1,2,2,3,4,6}
array[1]=2,同理inversions=index-0=1个
同理,对于之后的inversions分别是 3,4,1,0,,0,总计5+1+3+4+1=14个
*******************************************************************************************************************************************************
最后说明,对于一系列数来说,他们的所有排列的逆序对的期望是:n(n-1)/4。