Arrays · Core Array Techniques

Counting via Merge Sort

Piggybacking a count onto the merge step of merge sort so a quantity that looks like it needs an O(n^2) comparison of every pair can be counted in O(n log n).

  • 6 min read
  • 11 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: a scaled comparison needs its own pass
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

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:

  1. The pattern: count during the merge, while both halves are sorted.
  2. A second flavor: when the counting condition differs from the sort order, count in its own pass.
  3. Where "how out of order is this?" is a real metric.
  4. 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 waitingRight still waitingTakePairs counted
2 5 81 6 71 (right)3: 2, 5 and 8 are all bigger than 1
2 5 86 72 (left)0
5 86 75 (left)0
86 76 (right)1: 8 is bigger than 6
877 (right)1: 8 is bigger than 7
88 (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 needUse
Number of inversionsmerge 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 itmerge sort on indices, or a Fenwick tree
Inversions when values fit a small rangea Fenwick tree by value
Minimum adjacent swaps to sortthe inversion count
How much two rankings disagreeinversions of one ranking in the other's order

Common mistakes

MistakeWhat happensDo this instead
Not writing the merged half backhigher merges see unsorted halves; counts go wrongcopy the merged buffer back every time
An int counter in Javaoverflows past about 65,000 elementsa long
Counting on equal elements (< instead of <=)equal pairs counted as inversionstake from the left on ties
Counting reverse pairs inside the mergethe scaled condition doesn't follow merge ordera separate two-pointer pass
2 * a[j] in intoverflow flips the comparisoncompute 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 long for the count.
  • A condition the merge doesn't compare directly, like > 2 *, needs its own pass over the sorted halves.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
Inversion countmerge sort, count mid - i
Reverse pairsa separate pass before each merge
Per-element countsmerge indices, or a Fenwick tree
Online / small value rangea Fenwick tree
Fewest adjacent swapsthe inversion count

Snippets worth knowing by heart

Java

int i = lo, j = mid, k = lo;
while (i < mid && j < hi) {
    if (a[i] <= a[j]) buf[k++] = a[i++];                  // ties: take the left one
    else { count += mid - i; buf[k++] = a[j++]; }         // a[j] is below all of left[i..mid)
}
while (i < mid) buf[k++] = a[i++];
while (j < hi) buf[k++] = a[j++];
System.arraycopy(buf, lo, a, lo, hi - lo);                // write back - the next merge relies on it

Python

out, i, j = [], 0, 0
while i < len(left) and j < len(right):
    if left[i] <= right[j]:            # ties: take the left one
        out.append(left[i]); i += 1
    else:
        count += len(left) - i         # right[j] is below all of left[i:]
        out.append(right[j]); j += 1
out += left[i:] + right[j:]

The traps

  • No write-back: higher merges see unsorted halves, and counts go wrong. Copy the merged buffer back every time.
  • int counters: inversion counts overflow past about 65,000 elements. Use a long.
  • < instead of <= on ties: equal pairs count as inversions. Take from the left on ties.
  • Reverse pairs inside the merge: the scaled condition doesn't follow merge order. Count in a separate two-pointer pass.
  • 2 * a[j] overflow: the comparison flips in int. Compute in long.

Staff-level points, one line each

  • Each pair is cross-half at exactly one merge.
  • Merge, Fenwick and bubble-sort swaps all gave 38,487 on 400 values.
  • Inversions = minimum adjacent swaps = Kendall tau distance.
  • Reversed 100,000: 4,999,950,000; an int counter said 704,982,704.

Say it in an interview

  • "Both halves are sorted during the merge, so one comparison counts a whole block of pairs."
  • "Pairs within a half were counted by the recursive call."
  • "It's O(n log n) time and O(n) extra space for the buffer."
  • "The count can reach n(n-1)/2, so I keep it in a long."

Practice

This skill comes with 11 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Counting via Merge Sort free

Inversion of Array

HardProCoding problemJava · Python

Given an array nums, count the number of inversions: pairs of indices (i, j) with i < j and nums[i] > nums[j].

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Reverse Pairs

HardProCoding problemJava · Python

Count the pairs of positions i < j where the earlier value is more than double the later one: nums[i] > 2 * nums[j].

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Counting via Merge Sort free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime