《剑指offer》刷题——【时间效率与空间效率的平衡】面试题51:数组中的逆序对(java实现)
一、题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一
个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出
P%1000000007
二、题目分析
方法一:遍历-O(n^2)
- 顺序扫描整个数组,
- 每扫描一个数字,逐个比较该数字和它后面的数字的大小
- 若后面的数字比它小,则这两个数字就组成一个逆序对
方法二:归并思想,时间负责度O(nlogn),空间复杂度O(n)
- 将数组分成前后两个子数组,
- 统计子数组内部的逆序对数目,
- 统计出两个相邻子数组之间的逆序对的数目


- 合并、排序并统计逆序对过程:
- 用两个指针分别指向两个子数组的末尾,每次比较指针指向数字
- 每次比较,都把较大的数字从后往前复制到一个辅助数组,确保辅助数组中的数字是递增排序,再把较大的数字复制到辅助数组后,把辅助数组的指针向前移动一位,进行下次比较
- 若第一个子数组中数字 > 第二个子数组中数字,则构成逆序对,逆序对数=第二个子数组中剩余数字个数
- 若第一个子数组中数字 < 第二个子数组中数字,则不构成逆序对
三、代码实现
public class Solution{
private int count = 0;
public int InversePairs(int[] a) {
if (a == null || a.length == 0)
return -1;
mergeSort(a, 0, a.length - 1);
return count;
}
public void mergeSort(int[] a, int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(a, left, mid);
mergeSort(a, mid + 1, right);
merge(a, left, mid, right);
}
}
public void merge(int[] a, int left, int mid, int right) {
int[] tmp = new int[right - left + 1];
int t = right - left;
int l = mid;
int r = right;
while (l >= left && r >= mid + 1) {
if (a[l] > a[r]) {
count += (r - mid);
tmp[t--] = a[l--];
if (count >= 1000000007) {
count %= 1000000007;
}
}
else {
tmp[t--] = a[r--];
}
}
while (l >= left) {
tmp[t--] = a[l--];
}
while (r >= mid + 1) {
tmp[t--] = a[r--];
}
for (int i = 0; i <= right - left; i++) {
a[left + i] = tmp[i];
}
}
}