Chapter 19: Selection, Order Statistics and Ranking
19.06Beginner / Intermediate3 topics20 min1 interactive animation

Inversions and counts

An inversion is a pair of elements that are out of order. Counting inversions measures how unsorted an array is, and merge sort counts them for free while it sorts. The same idea solves reverse pairs and count-smaller-after-self.

Learning goals

  • Define an inversion and relate it to insertion sort and bubble sort
  • Count inversions in O(n log n) during merge sort
  • Adapt the counting step for reverse pairs
  • Compute, for each element, how many smaller elements follow it

Prerequisites

Merge sortDivide and conquerTwo pointers

How different are two rankings of the same movies? Count the pairs the two people ordered differently. That count is an inversion count, and a merge sort can compute it in O(n log n) rather than checking every pair.

19.06.1

Count inversions via merge sort

An inversion is a pair of indices i < j with arr[i] > arr[j]. A sorted array has 0 inversions, a reverse sorted array of n elements has n(n - 1)/2. The count also equals the number of swaps bubble sort makes and the number of shifts insertion sort makes.

While merging two sorted halves, whenever you take an element from the right half before the remaining left elements, it is smaller than all of them and was after all of them in the original order. So it forms an inversion with each of the mid - i + 1 remaining left elements. Add that count and keep merging. The animation shows this count growing in the inversions counter.

Check the example [2, 4, 1, 3, 5]: the inversions are (2,1), (4,1) and (4,3), so the answer is 3, which the second preset reproduces.

Java
1static long inversions;
2
3static void merge(int[] arr, int[] buf, int lo, int mid, int hi) {
4 System.arraycopy(arr, lo, buf, lo, hi - lo + 1);
5 int i = lo, j = mid + 1;
6 for (int k = lo; k <= hi; k++) {
7 if (i > mid) arr[k] = buf[j++];
8 else if (j > hi) arr[k] = buf[i++];
9 else if (buf[j] < buf[i]) { arr[k] = buf[j++]; inversions += mid - i + 1; } // right jumps over (mid - i + 1) left elements
10 else arr[k] = buf[i++];
11 }
12}

Worst

O(n log n)

Space

O(n)

Count inversions with merge sort

Interactive · edit the input

Your input

Try

int[] arr · length 8

38
27
43
3
9
82
10
15
01234567
Being comparedSorted runOutside this call
Setup

Divide the 8 elements until single elements remain, then merge sorted runs back together.

Step 1 / 94
Java
1public static void mergeSort(int[] arr, int lo, int hi) {
2 if (hi - lo < 1) return;
3 int mid = lo + (hi - lo) / 2;
4 mergeSort(arr, lo, mid);
5 mergeSort(arr, mid + 1, hi);
6 merge(arr, lo, mid, hi);
7}
8
9static void merge(int[] arr, int lo, int mid, int hi) {
10 int[] tmp = new int[hi - lo + 1];
11 int i = lo, j = mid + 1, k = 0;
12 while (i <= mid && j <= hi) {
13 if (arr[i] <= arr[j]) tmp[k++] = arr[i++];
14 else { tmp[k++] = arr[j++]; inversions += mid - i + 1; }
15 }
16 while (i <= mid) tmp[k++] = arr[i++];
17 while (j <= hi) tmp[k++] = arr[j++];
18 System.arraycopy(tmp, 0, arr, lo, tmp.length);
19}

Live state

0 comparisons0 inversions

n

8

No condition evaluated on this step
Execution trace (dry run table)Show 94 steps
StepLineStateWhat happens
1L1n=8Divide the 8 elements until single elements remain, then merge sorted runs back together.
2L1lo=0 hi=7 size=8Enter mergeSort(arr, 0, 7) on 8 elements.
3L2lo=0 hi=7More than one element, so split in half.
4L3lo=0 mid=3 hi=7mid = 3. Left half [0..3], right half [4..7].
5L4lo=0 hi=3 size=4Enter mergeSort(arr, 0, 3) on 4 elements.
6L2lo=0 hi=3More than one element, so split in half.
7L3lo=0 mid=1 hi=3mid = 1. Left half [0..1], right half [2..3].
8L4lo=0 hi=1 size=2Enter mergeSort(arr, 0, 1) on 2 elements.
9L2lo=0 hi=1More than one element, so split in half.
10L3lo=0 mid=0 hi=1mid = 0. Left half [0..0], right half [1..1].
11L4lo=0 hi=0 size=1Enter mergeSort(arr, 0, 0) on 1 element.
12L2lo=0 hi=0Base case: one element is already sorted.
13L5lo=1 hi=1 size=1Enter mergeSort(arr, 1, 1) on 1 element.
14L2lo=1 hi=1Base case: one element is already sorted.
15L10lo=0 mid=0 hi=1 i=0 j=1 k=0Allocate tmp for 2 elements. Left run [0..0] and right run [1..1] are each already sorted.
16L13lo=0 mid=0 hi=1 i=0 j=1 k=0The right head is strictly smaller, so take it.
17L14lo=0 mid=0 hi=1 i=0 j=2 k=1tmp[0] = 27. It jumps ahead of the 1 remaining left element, and each of those is larger, so that adds 1 inversion.
18L16lo=0 mid=0 hi=1 i=1 j=2 k=2The left run still has 1 element. They are already sorted and no larger run remains, so copy them all.
19L18lo=0 mid=0 hi=1 i=1 j=2 k=2Copy tmp back into arr[0..1]. That range is now sorted.
20L5lo=2 hi=3 size=2Enter mergeSort(arr, 2, 3) on 2 elements.
21L2lo=2 hi=3More than one element, so split in half.
22L3lo=2 mid=2 hi=3mid = 2. Left half [2..2], right half [3..3].
23L4lo=2 hi=2 size=1Enter mergeSort(arr, 2, 2) on 1 element.
24L2lo=2 hi=2Base case: one element is already sorted.
25L5lo=3 hi=3 size=1Enter mergeSort(arr, 3, 3) on 1 element.
26L2lo=3 hi=3Base case: one element is already sorted.
27L10lo=2 mid=2 hi=3 i=2 j=3 k=0Allocate tmp for 2 elements. Left run [2..2] and right run [3..3] are each already sorted.
28L13lo=2 mid=2 hi=3 i=2 j=3 k=0The right head is strictly smaller, so take it.
29L14lo=2 mid=2 hi=3 i=2 j=4 k=1tmp[0] = 3. It jumps ahead of the 1 remaining left element, and each of those is larger, so that adds 1 inversion.
30L16lo=2 mid=2 hi=3 i=3 j=4 k=2The left run still has 1 element. They are already sorted and no larger run remains, so copy them all.
31L18lo=2 mid=2 hi=3 i=3 j=4 k=2Copy tmp back into arr[2..3]. That range is now sorted.
32L10lo=0 mid=1 hi=3 i=0 j=2 k=0Allocate tmp for 4 elements. Left run [0..1] and right run [2..3] are each already sorted.
33L13lo=0 mid=1 hi=3 i=0 j=2 k=0The right head is strictly smaller, so take it.
34L14lo=0 mid=1 hi=3 i=0 j=3 k=1tmp[0] = 3. It jumps ahead of the 2 remaining left elements, and each of those is larger, so that adds 2 inversions.
35L13lo=0 mid=1 hi=3 i=0 j=3 k=1The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
36L13lo=0 mid=1 hi=3 i=1 j=3 k=2tmp[1] = 27.
37L13lo=0 mid=1 hi=3 i=1 j=3 k=2The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
38L13lo=0 mid=1 hi=3 i=2 j=3 k=3tmp[2] = 38.
39L17lo=0 mid=1 hi=3 i=2 j=4 k=4The right run still has 1 element. They are already sorted and no larger run remains, so copy them all.
40L18lo=0 mid=1 hi=3 i=2 j=4 k=4Copy tmp back into arr[0..3]. That range is now sorted.
41L5lo=4 hi=7 size=4Enter mergeSort(arr, 4, 7) on 4 elements.
42L2lo=4 hi=7More than one element, so split in half.
43L3lo=4 mid=5 hi=7mid = 5. Left half [4..5], right half [6..7].
44L4lo=4 hi=5 size=2Enter mergeSort(arr, 4, 5) on 2 elements.
45L2lo=4 hi=5More than one element, so split in half.
46L3lo=4 mid=4 hi=5mid = 4. Left half [4..4], right half [5..5].
47L4lo=4 hi=4 size=1Enter mergeSort(arr, 4, 4) on 1 element.
48L2lo=4 hi=4Base case: one element is already sorted.
49L5lo=5 hi=5 size=1Enter mergeSort(arr, 5, 5) on 1 element.
50L2lo=5 hi=5Base case: one element is already sorted.
51L10lo=4 mid=4 hi=5 i=4 j=5 k=0Allocate tmp for 2 elements. Left run [4..4] and right run [5..5] are each already sorted.
52L13lo=4 mid=4 hi=5 i=4 j=5 k=0The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
53L13lo=4 mid=4 hi=5 i=5 j=5 k=1tmp[0] = 9.
54L17lo=4 mid=4 hi=5 i=5 j=6 k=2The right run still has 1 element. They are already sorted and no larger run remains, so copy them all.
55L18lo=4 mid=4 hi=5 i=5 j=6 k=2Copy tmp back into arr[4..5]. That range is now sorted.
56L5lo=6 hi=7 size=2Enter mergeSort(arr, 6, 7) on 2 elements.
57L2lo=6 hi=7More than one element, so split in half.
58L3lo=6 mid=6 hi=7mid = 6. Left half [6..6], right half [7..7].
59L4lo=6 hi=6 size=1Enter mergeSort(arr, 6, 6) on 1 element.
60L2lo=6 hi=6Base case: one element is already sorted.
61L5lo=7 hi=7 size=1Enter mergeSort(arr, 7, 7) on 1 element.
62L2lo=7 hi=7Base case: one element is already sorted.
63L10lo=6 mid=6 hi=7 i=6 j=7 k=0Allocate tmp for 2 elements. Left run [6..6] and right run [7..7] are each already sorted.
64L13lo=6 mid=6 hi=7 i=6 j=7 k=0The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
65L13lo=6 mid=6 hi=7 i=7 j=7 k=1tmp[0] = 10.
66L17lo=6 mid=6 hi=7 i=7 j=8 k=2The right run still has 1 element. They are already sorted and no larger run remains, so copy them all.
67L18lo=6 mid=6 hi=7 i=7 j=8 k=2Copy tmp back into arr[6..7]. That range is now sorted.
68L10lo=4 mid=5 hi=7 i=4 j=6 k=0Allocate tmp for 4 elements. Left run [4..5] and right run [6..7] are each already sorted.
69L13lo=4 mid=5 hi=7 i=4 j=6 k=0The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
70L13lo=4 mid=5 hi=7 i=5 j=6 k=1tmp[0] = 9.
71L13lo=4 mid=5 hi=7 i=5 j=6 k=1The right head is strictly smaller, so take it.
72L14lo=4 mid=5 hi=7 i=5 j=7 k=2tmp[1] = 10. It jumps ahead of the 1 remaining left element, and each of those is larger, so that adds 1 inversion.
73L13lo=4 mid=5 hi=7 i=5 j=7 k=2The right head is strictly smaller, so take it.
74L14lo=4 mid=5 hi=7 i=5 j=8 k=3tmp[2] = 15. It jumps ahead of the 1 remaining left element, and each of those is larger, so that adds 1 inversion.
75L16lo=4 mid=5 hi=7 i=6 j=8 k=4The left run still has 1 element. They are already sorted and no larger run remains, so copy them all.
76L18lo=4 mid=5 hi=7 i=6 j=8 k=4Copy tmp back into arr[4..7]. That range is now sorted.
77L10lo=0 mid=3 hi=7 i=0 j=4 k=0Allocate tmp for 8 elements. Left run [0..3] and right run [4..7] are each already sorted.
78L13lo=0 mid=3 hi=7 i=0 j=4 k=0The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
79L13lo=0 mid=3 hi=7 i=1 j=4 k=1tmp[0] = 3.
80L13lo=0 mid=3 hi=7 i=1 j=4 k=1The right head is strictly smaller, so take it.
81L14lo=0 mid=3 hi=7 i=1 j=5 k=2tmp[1] = 9. It jumps ahead of the 3 remaining left elements, and each of those is larger, so that adds 3 inversions.
82L13lo=0 mid=3 hi=7 i=1 j=5 k=2The right head is strictly smaller, so take it.
83L14lo=0 mid=3 hi=7 i=1 j=6 k=3tmp[2] = 10. It jumps ahead of the 3 remaining left elements, and each of those is larger, so that adds 3 inversions.
84L13lo=0 mid=3 hi=7 i=1 j=6 k=3The right head is strictly smaller, so take it.
85L14lo=0 mid=3 hi=7 i=1 j=7 k=4tmp[3] = 15. It jumps ahead of the 3 remaining left elements, and each of those is larger, so that adds 3 inversions.
86L13lo=0 mid=3 hi=7 i=1 j=7 k=4The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
87L13lo=0 mid=3 hi=7 i=2 j=7 k=5tmp[4] = 27.
88L13lo=0 mid=3 hi=7 i=2 j=7 k=5The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
89L13lo=0 mid=3 hi=7 i=3 j=7 k=6tmp[5] = 38.
90L13lo=0 mid=3 hi=7 i=3 j=7 k=6The left head is not larger, so take it. Using <= (left wins ties) is what makes merge sort stable.
91L13lo=0 mid=3 hi=7 i=4 j=7 k=7tmp[6] = 43.
92L17lo=0 mid=3 hi=7 i=4 j=8 k=8The right run still has 1 element. They are already sorted and no larger run remains, so copy them all.
93L18lo=0 mid=3 hi=7 i=4 j=8 k=8Copy tmp back into arr[0..7]. That range is now sorted.
94L6Sorted. Total inversions counted: 15 (pairs i < j with arr[i] > arr[j]).
19.06.2

Reverse pairs

Reverse pairs counts pairs i < j with nums[i] > 2 * nums[j]. The condition is not the same as the merge condition, so count in a separate loop before merging, while both halves are sorted.

For each i in the left half in increasing order, advance j through the right half while nums[i] > 2 * nums[j]. Because nums[i] only grows, j never moves backwards, so the loop is linear. Then run the normal merge. The multiplication must be done in long, since 2 * nums[j] can overflow int.

Java
1static int reversePairs(int[] nums) {
2 return sort(nums, new int[nums.length], 0, nums.length - 1);
3}
4
5private static int sort(int[] a, int[] buf, int lo, int hi) {
6 if (lo >= hi) return 0;
7 int mid = lo + (hi - lo) / 2;
8 int count = sort(a, buf, lo, mid) + sort(a, buf, mid + 1, hi);
9 int j = mid + 1;
10 for (int i = lo; i <= mid; i++) { // both halves are sorted here
11 while (j <= hi && (long) a[i] > 2L * a[j]) j++;
12 count += j - (mid + 1);
13 }
14 merge(a, buf, lo, mid, hi); // ordinary merge afterwards
15 return count;
16}

Worst

O(n log n)

Space

O(n)

19.06.3

Count smaller elements after self

For each index i, count the elements to its right that are smaller than nums[i]. This is an inversion count per element. Sort indices instead of values, so each element keeps its identity as the merge moves it.

In each merge, track how many right-half elements have been placed so far. When a left element is placed, every right element already placed is smaller than it and was originally to its right, so add that number to the left element's answer. Take the left element on ties, so only strictly smaller right elements are counted.

numsSmaller elements to the right
[5, 2, 6, 1][2, 1, 1, 0]
[-1, -1][0, 0]
[3, 2, 1][2, 1, 0]
Java
1static List<Integer> countSmaller(int[] nums) {
2 int n = nums.length;
3 int[] idx = new int[n], tmp = new int[n], ans = new int[n];
4 for (int i = 0; i < n; i++) idx[i] = i;
5 sort(nums, idx, tmp, ans, 0, n - 1);
6 return Arrays.stream(ans).boxed().toList();
7}
8
9private static void sort(int[] nums, int[] idx, int[] tmp, int[] ans, int lo, int hi) {
10 if (lo >= hi) return;
11 int mid = lo + (hi - lo) / 2;
12 sort(nums, idx, tmp, ans, lo, mid);
13 sort(nums, idx, tmp, ans, mid + 1, hi);
14 int i = lo, j = mid + 1, k = lo, rightPlaced = 0;
15 while (i <= mid || j <= hi) {
16 if (j > hi || (i <= mid && nums[idx[i]] <= nums[idx[j]])) {
17 ans[idx[i]] += rightPlaced; // smaller elements from the right half
18 tmp[k++] = idx[i++];
19 } else {
20 rightPlaced++;
21 tmp[k++] = idx[j++];
22 }
23 }
24 System.arraycopy(tmp, lo, idx, lo, hi - lo + 1);
25}

Worst

O(n log n)

Space

O(n)

Practice ladder

Now try it yourself

Easy

  • Count inversions in an array of at most 10 elements by brute force
  • Is an array sorted, and how far is it from sorted?

Medium

  • Count inversions in O(n log n)
  • Reverse pairs
  • Count of smaller numbers after self

Advanced

  • Count range sums with merge sort
  • Global and local inversions
  • Count inversions with a Fenwick tree and compare with the merge version

Key takeaways

  • An inversion is a pair i < j with arr[i] > arr[j]; the count equals bubble sort's swaps and insertion sort's shifts.
  • Merge sort counts them in O(n log n): when a right element is taken first, it inverts with all remaining left elements.
  • Reverse pairs counts in a pass before merging; count-smaller sorts indices and uses the number of right elements placed so far.