On this page
Description
Merge-sort counting answers "how many pairs are out of order?" in O(n log n). Those pairs are called inversions, and variants such as reverse pairs work the same way. You count while merge sort runs. When two sorted halves are merged, the left-half elements still waiting are all bigger than the right-half element being placed. Each of them forms an out-of-order pair with it, so a whole block of pairs is counted in one step.
We'll go from simple to more technical:
- The pattern: count during the merge, while both halves are sorted.
- A second flavor: when the counting condition differs from the sort order, count in its own pass.
- Where "how out of order is this?" is a real metric.
- The bugs that only show up on large or deep inputs.
The approach
Checking every pair is O(n^2): about 5 billion comparisons for 100,000 elements. Merge sort does better because its merge step is where left-half and right-half elements meet. Both halves are sorted at that point. So when a right-half element is smaller than the next left-half element, it's also smaller than every left-half element after that one.
Merging the sorted halves [2, 5, 8] and [1, 6, 7]:
| Left still waiting | Right still waiting | Take | Pairs counted |
|---|---|---|---|
| 2 5 8 | 1 6 7 | 1 (right) | 3: 2, 5 and 8 are all bigger than 1 |
| 2 5 8 | 6 7 | 2 (left) | 0 |
| 5 8 | 6 7 | 5 (left) | 0 |
| 8 | 6 7 | 6 (right) | 1: 8 is bigger than 6 |
| 8 | 7 | 7 (right) | 1: 8 is bigger than 7 |
| 8 | 8 (left) | 0 |
That's 5 pairs found with 6 steps, not 9 pair checks.
Java
long mergeSortCount(int[] nums, int lo, int hi) {
if (lo >= hi) return 0;
int mid = lo + (hi - lo) / 2;
long count = mergeSortCount(nums, lo, mid) + mergeSortCount(nums, mid + 1, hi);
return count + mergeCount(nums, lo, mid, hi); // cross-half pairs, counted during the merge
}
long mergeCount(int[] nums, int lo, int mid, int hi) {
// standard merge, except: whenever an element from the right half is taken
// because it's smaller than the current left element, every remaining
// element in the left half (there are `mid - i + 1` of them) forms an
// out-of-order pair with it - add that count.
}
Python
def merge_sort_count(nums: list[int], lo: int, hi: int) -> int:
if lo >= hi:
return 0
mid = (lo + hi) // 2
count = merge_sort_count(nums, lo, mid) + merge_sort_count(nums, mid + 1, hi)
return count + merge_count(nums, lo, mid, hi) # cross-half pairs, counted during the merge
def merge_count(nums: list[int], lo: int, mid: int, hi: int) -> int:
# standard merge, except: whenever an element from the right half is taken
# because it's smaller than the current left element, every remaining
# element in the left half (there are mid - i + 1 of them) forms an
# out-of-order pair with it - add that count.
...
Pairs inside one half were already counted by the recursive call that sorted that half. So the merge only counts pairs across the two halves, and each count is one subtraction, mid - i + 1, not a loop.
A second flavor: a scaled comparison needs its own pass
Reverse Pairs counts pairs where nums[i] > 2 * nums[j], with i in the left half and j in the right half. The merge only ever compares nums[i] with nums[j], so it doesn't answer this question. Instead, run a separate two-pointer pass over the two sorted halves before the normal merge:
Java
int j = mid + 1;
for (int i = lo; i <= mid; i++) {
while (j <= hi && nums[i] > 2L * nums[j]) j++;
count += j - (mid + 1); // every right-half element scanned past so far qualifies with nums[i]
}
// ...then merge lo..hi normally, exactly as in plain inversion counting
Python
j = mid + 1
for i in range(lo, mid + 1):
while j <= hi and nums[i] > 2 * nums[j]:
j += 1
count += j - (mid + 1) # every right-half element scanned past so far qualifies with nums[i]
# ...then merge lo..hi normally, exactly as in plain inversion counting
j never moves back. Both halves are sorted, so as i moves right, nums[i] only grows. Any right-half element it already beat, it still beats. That keeps the extra pass O(n) per level.
Where you'll see it
"How far is this data from sorted?" is a real measure. For example, how far does an event log stray from the order in which events arrived? This technique answers it in O(n log n). It also measures how much two rankings disagree.
Which tool for which job?
| You need | Use |
|---|---|
| Number of inversions | merge sort with counting |
Pairs with a[i] > 2 * a[j] (reverse pairs) | a separate counting pass before each merge |
| For each element, how many smaller ones come after it | merge sort on indices, or a Fenwick tree |
| Inversions when values fit a small range | a Fenwick tree by value |
| Minimum adjacent swaps to sort | the inversion count |
| How much two rankings disagree | inversions of one ranking in the other's order |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Not writing the merged half back | higher merges see unsorted halves; counts go wrong | copy the merged buffer back every time |
An int counter in Java | overflows past about 65,000 elements | a long |
Counting on equal elements (< instead of <=) | equal pairs counted as inversions | take from the left on ties |
| Counting reverse pairs inside the merge | the scaled condition doesn't follow merge order | a separate two-pointer pass |
2 * a[j] in int | overflow flips the comparison | compute in long |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. A merge counts correctly but never writes the merged result back into the array. What breaks?
Show answer
Every count at a higher level. Those merges assume their two halves are sorted, and that's what makes the mid - i + 1 shortcut valid. If a lower level never wrote its sorted result back, the assumption fails silently.
2. Why does the count need a long for 100,000 elements?
Show answer
The most pairs there can be is n(n - 1)/2, about 5 billion for 100,000 elements. An int tops out at about 2.1 billion, so it would overflow and wrap to a wrong value.
3. Why does Reverse Pairs need an extra pass when plain inversion counting doesn't?
Show answer
Plain inversions use nums[i] > nums[j], the same comparison the merge already makes, so the count comes for free. Reverse Pairs uses nums[i] > 2 * nums[j], which the merge never checks. A separate pass over the sorted halves counts it, still in O(n) per level.
Key takeaways
- When a right-half element is taken, every left-half element still waiting is bigger. Count them all at once with
mid - i + 1. - The merge counts only pairs across the halves. The recursion already counted pairs inside each half.
- Write the merged result back, because higher levels rely on sorted halves.
- Use a
longfor the count. - A condition the merge doesn't compare directly, like
> 2 *, needs its own pass over the sorted halves.