目录
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如数组{7, 5, 6, 4}中,一共存在5个逆序对,分别是(7, 6)、(7, 5)、(7, 4)、(6, 4)、(5, 4)。
测试用例
- 功能测试(输入未经排序的数组、递增排序的数组、递减排序的数组;输入的数组中包含重复的数字)
- 边界值测试(输入的数组中只有两个数字;输入的数组只有一个数字)
- 特殊输入测试(表示数组的指针为空指针)
题目考点
- 考察应聘者分析复杂问题的能力。
- 考察应聘者对归并排序的掌握程度。
解题思路
实质上,利用归并排序实现,时间复杂度为O(nlogn),空间复杂度O(n);
1、先把数组分隔成子数组,分别统计子数组内部 + 相邻子数组之间 的逆序对;
2、在统计逆序对的时候,对子数组排序。
如数组{7, 5, 6, 4}中
- 把长度为4的数组分解成两个长度为2的子数组;
- 把长度为2的数组分解成两个成都为1的子数组;
- 把长度为1的子数组合并、排序并统计逆序对,得到(7, 5)、(6, 4);
- 把长度为2的子数组合并、排序并统计逆序对,得到(7, 6)、(7, 4)、(5, 4);
参考解题
public class Solution {
// 归并排序
public int InversePairs(int [] array) {
// 异常
if(array == null || array.length < 0){
return 0;
}
// 辅助数组
int[] temp = new int[array.length];
for(int i = 0; i < array.length; ++i ){
temp[i] = array[i];
}
int count = mergeSort(array, temp, 0, array.length - 1);
return count;
}
// 归并排序
public int mergeSort(int[] array, int[] temp, int low, int high){
// 异常
if(low == high){
return 0;
}
int mid = (low + high) >> 1;
// -------子数组内部---------------------------------------------
int left = mergeSort(array, temp, low, mid) % 1000000007;
int right = mergeSort(array, temp, mid + 1, high) % 1000000007;
// -------子数组之间---------------------------------------------
int count = 0;
// 子数组左边最右端下标
int i = mid;
// 子数组右边最右端下标
int j = high;
// 辅助数组最右端下标
int k = high;
while(i >= low && j >= mid + 1){
if(array[i] > array[j]){
temp[k--] = array[i--];
count += j - mid;
if(count >= 1000000007)//数值过大求余
{
count %= 1000000007;
}
}else{
temp[k--] = array[j--];
}
}
// 如果左边有剩余元素,移入辅助数组
for(; i >= low ; --i){
temp[k--] = array[i];
}
// 如果右边有剩余元素,移入辅助数组
for(; j >= mid + 1 ; --j){
temp[k--] = array[j];
}
// 临时数组覆盖原数组???
System.arraycopy(temp, low, array, low, high - low + 1);
// 子数组内部 + 子数组之间
return (left + right + count) % 1000000007;
}
}
归并排序