An interactive visualization of modified merge sort for counting inversions. Watch how inversions are detected during the merge step when an element from the right subarray is placed before remaining elements in the left subarray.
Divide and Conquer LeetCode 315 โ Hardarr, count the number of inversion pairs (i, j)
where i < j but arr[i] > arr[j].fun countInversions(arr: List<Int>): Pair<List<Int>, Int> { if (arr.size <= 1) return Pair(arr, 0) val mid = arr.size / 2 val (left, leftInv) = countInversions(arr.subList(0, mid)) val (right, rightInv) = countInversions(arr.subList(mid, arr.size)) val (merged, splitInv) = mergeCount(left, right) return Pair(merged, leftInv + rightInv + splitInv)}fun mergeCount(left: List<Int>, right: List<Int>): Pair<List<Int>, Int> { val result = mutableListOf<Int>() var inversions = 0 var i = 0; var j = 0 while (i < left.size && j < right.size) { if (left[i] <= right[j]) { result.add(left[i]) i++ } else { // inversion found! result.add(right[j]) inversions += left.size - i j++ } } result.addAll(left.subList(i, left.size) + right.subList(j, right.size)) return Pair(result, inversions)
During the merge step of merge sort, when we pick an element from the right subarray
before all elements in the left subarray have been placed, each such pick means that element is smaller than all
remaining elements in the left subarray. These are exactly the split inversions โ
we add len(left) - i to the inversion count (where i is the current index in the left subarray).
left_inv) and right half (right_inv).left[i] > right[j], all remaining elements in the left subarray (left[i..end]) form inversions with right[j].left_inv + right_inv + split_inv.Same as standard merge sort โ we do O(n) work at each of O(log n) levels of recursion. The auxiliary space is O(n) for the temporary arrays used during merging.