题目
在数组中的两个数字假设前面一个数字大于后面的数字。则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
比如:{7,5,6,4},一共存在5个逆序对,各自是(7,6)(7,5)(7,4)(6,4)和(5,4)
思路
本来是毫无思路的,一般习惯了暴力破解,剑指offer后面的归并排序的思想。让我想到归并排序是好多年前写过的(忘了......果然出来混总是要还的)。
归并排序的思想是利用分治法。将一个非常大的问题,缩小成一个非常小的问题,将非常长的数组。终于递归分成两个数进行比較。
逆序对刚好能够利用这一点。当剩两个数的时候。推断前面的是否大于后面的,假设大于后面的。那么就是一个逆序对,并且后面的一般都是排好序的。所以我们让指针从后面開始往前遍历,仅仅要前半部分的数据大于后半部分的最后一个,那么就会大于全部后半部分的。
如图:
(图片来自:http://blog.csdn.net/cjf_iceking/article/details/7920153)
第一层排好序后,回到第二层。这时候我们推断95 和84的大小。95>84。就说明95大于84之前全部的,所以此时的逆序对数就是84加上之前全部的元素个数。
然后计算完毕后,反着放入新数组。这样后边就不会再次累积了,这事实上借用了归并排序的思想。
代码
public static int InversePair(int[] data,int start,int end,int[] temp) { int count=0; //累积逆序对的总数 if(start < end) { int mid = (end+start)/2; count += InversePair(data, start, mid ,temp); count += InversePair(data,mid+1,end,temp); count += getInversePair(data,start,mid,end,temp); } return count; } private static int getInversePair(int[] data, int start,int mid, int end,int[] temp) { int i=mid; //前半部分的末尾 int count=0; int j = end; //后半部分的末尾 int index=0; while(i >=start && j>mid) { if(data[i] > data[j]) { temp[index++] = data[i--]; count += j-mid; //j之前的全部元素都是逆序对数 } else { temp[index++] = data[j--]; } } while(i>=start) { temp[index++] = data[i--]; } while(j>mid) { temp[index++] = data[j--]; } //将其排好序的放入原数组 for(int k=0;k<index;k++) { data[end-k] = temp[k]; } return count; }