题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,
求出这个数组中的逆序对的总数P
思路:以下是剑指中的思路。最容易想到是就是顺序扫描整个数组,每扫描到一个数字,逐个比较这个数字和它后面数字的大小。这种思路的时间复杂度是.剑指提供了一种比较快的思路,本质上是归并排序。我们先把数组分隔为子数组,统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对。在统计的过程中,还需要对数组进行排序。
归并排序的时间复杂度是O(nlogn),比上面提到的方法要快,但是同时归并排序需要一个长度为n的辅助数组,相当于使用O(n)的空间消耗换来了时间效率的提升,因此是一种空间换时间的算法。
代码:
int count = 0;
public int InversePairs(int [] array) {
if(array==null)
return 0;
mergeSort(array,0,array.length-1);
return count;
}
public static void mergeSort(int[] data,int start,int end) {
//分治的思想,将数组对半划分
int mid = (start + end) / 2;
if (start < end) {
mergeSort(data, start, mid); //左边部分
mergeSort(data, mid + 1, end); //右边部分
merge(data, start, mid, end); //左右归并两部分
}
}
//start是左数组第一个元素的索引,mid是左数组最后一个元素的索引,mid+1是右数组第一个元素的索引,end是右数组最后一
//个元素的索引
public static void merge(int[] data,int start,int mid,int end) {
int arr[] = new int[end - start + 1]; //创建一个辅助数组
int c = 0; //辅助数组的指针
int s = start; //保存最开始左边数组的初始位置
int index = mid + 1;
while (start <= mid && index <= end) {
if (data[start] < data[index]) {
arr[c++] = data[start++];
} else {
arr[c++] = data[index++];
count += mid +1 - start; //逆序对的个数为左边数组目前的长度,start为当前左数组的索引,mid为左数组的最后一个元素。
}
}
//将最后剩的那个元素添加到辅助数组中
while (start <= mid) {
arr[c++] = data[start++];
}
while (index <= end) {
arr[c++] = data[index++];
}
//将辅助数组的元素复制回原始数组
for (int d : arr) {
data[s++] = d;
}
}