题目: 给出一列数,a1, a2,....到 an,求它的逆序对数。逆序对就是 下标 i 比 j小,但是值 ai 却比 a j大。n 可以高大 10万。
思路:
(1)穷举,肯定不现实的,因为n 可以高达10万,肯定超时;
(2)考录分治法,受到归并排序的启发,能不能不断划分数组到最小,然后合并时统计 逆序对数。划分和递归都和归并排序没什么区别,问题在合并时如何统计。
合并左右两个数组时,左边 的数组的下标肯定要比右边数组的下标小,那么如果右边数组有比左边数组小的值,比如 [ 5,6] 和 [ 1 ] 合并,那么 5和6 肯定就是1的逆序对组合数了。因此,在每次把右边数组移入临时数组前,左边数组的个数就是该移入数的逆序对数。
代码:
/**
* @author snail
* @time 2014-9-11下午09:03:03
* TODO
*/
public class SecondMerge {
private static int count = 0; //统计数组中的逆序对数
public static void main(String[] args) {
int[] test = {1212,99,6,4};
SecondMerge.partition(test);
System.out.println(count+"对 ");
System.out.println(" ");
for (int i : test) {
System.out.print(i+" ");
}
}
/**
* decription: 划分
* @author : linjq
*/
private static void partition(int[] list){
int length = list.length;
if (length <= 1) {
return ;
}
int firstLength = length >> 1 ;
int[] firstHalf = new int[firstLength];
System.arraycopy(list,0,firstHalf,0,firstLength);
//继续二分
partition(firstHalf);
int secondLength = length - firstLength;
int[] secondHalf = new int[secondLength];
System.arraycopy(list,firstLength,secondHalf,0,secondLength);
partition(secondHalf);
//合并
int[] resultList = merge(firstHalf,secondHalf);
System.arraycopy(resultList,0,list,0,resultList.length);
}
/**
* decription:合并
* @author : linjq
*/
private static int[] merge(int[] firstList,int[] secondList){
int firstLength = firstList.length;
int secondLength = secondList.length;
int i = 0,j=0,k=0;
int[] resultList = new int[firstLength + secondLength];
while( i<firstLength && j < secondLength ){
if(firstList[i] < secondList[j]){
resultList[k++] = firstList[i++];
}else{
//此时,左边还没来得及复制到临时数组中的元素,就是下标比右边小,值却比右边大的逆序值
count += (firstLength -i);
resultList[k++] = secondList[j++];
}
}
while(i<firstLength){
resultList[k++] = firstList[i++];
}
while(j < secondLength){
resultList[k++] = secondList[j++];
}
return resultList;
}
}