逆序对
关于归并排序和快速排序的第一个衍生问题就是逆序对,例如下图中的数组{8,6,2,3,1,5,7,4},其中{2,3}就是一个顺序对,而{2,1}就是一个逆序对。
归并排序
要解决此问题此时可以依赖于归并过程,例如以下动画,两个分别排好序的子数组{2,3,6,8,}和{1,4,5,7,}:
- 首先1比2小,意味1比2后面的所有元素都小,计数器可直接加4,指向1的下标后移。
- 4大于2,不考虑,指向2的下标后移。
- 4大于3,不考虑,指向3的下标后移。
- 4小于6,意味4比6后面的所有元素都小,计数器可直接加2,指向4的下标后移。
- 依次类推
public class InversionCount { private InversionCount() { } private static long merge(Comparable[] arr, int l, int mid, int r){ Comparable[] aux = Arrays.copyOfRange(arr, l, r+1); long res = 0L; int i = l, j = mid+1; for(int k = l; k <= r; k ++){ if(i > mid){ arr[k] = aux[j-l]; j++; }else if(j > r){ arr[k] = aux[i-l]; i ++; }else if(aux[i-l].compareTo(aux[j-l]) <= 0){ arr[k] = aux[i-l]; i ++; }else { //右边小于左边 arr[k] = aux[j-l]; j ++; // 重点的部分啦 res += (long)(mid - i + 1); } } return res; } private static long solve(Comparable[] arr, int l, int r){ if(l >= r) return 0L; int mid = (r-l)/2 + l; long res1 = solve(arr, l, mid); long res2 = solve(arr, mid+1, r); return res1 + res2 + merge(arr, l, mid, r); } public static long solve(Comparable[] arr){ int n = arr.length; return solve(arr, 0, n-1); } public static void main(String[] args) { Integer[] arr = {1,2,3,4,5,6,7,0}; System.out.println(solve(arr)); } }