这道题的解法是使用归并排序,理解的关键在于两个区间,求两个区间之间的逆序对,与这两个区间内的数字是否有序无关,这样:
- 自下而上的,先统计小的区间之间的逆序对;
- 由小的区间合并成更大的区间之间的逆序对
public class Solution { private int count; public int InversePairs(int [] a) { if (a == null || a.length == 0) { return 0; } int[] tmp = new int[a.length]; mergeSort(a, 0, a.length - 1, tmp); return count; } private void mergeSort(int[] a, int l, int r, int[] tmp) { if (l < r) { int m = (l + r) / 2; mergeSort(a, l, m, tmp); mergeSort(a, m + 1, r, tmp); merge(a, l, m, r, tmp); } } private void merge(int[] a, int l, int m, int r, int[] temp) { int k = 0, i = l, j = m + 1; while (i <= m && j <= r) { if(a[i] <= a[j]) { temp[k++] = a[i++]; } else { temp[k++] = a[j++]; count += m - i + 1; count = count % 1000000007; } } while(i <= m) { temp[k++] = a[i++]; } while(j <= r) { temp[k++] = a[j++]; } for(i = 0; i < k; ++i) { a[l + i] = temp[i]; } } }
几个关键的点:
- 每次对
count
增加新的计数之后,都要求模,否则可能数值过大 merge
函数中,计算逆序对的值时用的是m - i + 1
,思想是如果左区间的ai
大于右区间的aj
,那么从ai
到am
的每个数字都大于aj
,但是思考一个问题,为什么这里不用j
呢?因为是从右向左遍历右区间,因此如果我们想到:如果ai
>aj
,那么从a(m+1)
到aj
的每个数字都小于ai
,这个时候一方面存在重复的计算,即计算ai
和aj
的时候,已经计算了a(j-1)
了,但是这里还会重复计算一次;另一方面,由于合并的时候是一趟,因此ai
处理后,那些比ai
小的右区间中的数值已经被合并到了新的临时空间中了,因此ai
右边的左区间中的剩下的值没有处理,又存在遗漏,因此这种算法是不正确的,所以只能使用i
来计算。
总结起来这道题目并不简单,其中适用归并排序的点,和如何在归并排序中进行求解是理解本题的关键。