Skip to content
Feb 25

Algo: Counting Inversions with Merge Sort

MT
Mindli Team

AI-Generated Content

Algo: Counting Inversions with Merge Sort

Understanding how "out of order" a dataset is can be a crucial first step in analysis. Counting inversions provides a precise measure of this disorder, with applications ranging from analyzing ranking consistency to pre-processing for other algorithms. By cleverly modifying a classic sorting algorithm, we can count these inversions with the same efficiency as sorting itself, transforming a naive approach into an elegant solution.

What Are Inversions?

An inversion is a pair of indices within an array such that but the element at is greater than the element at . In simpler terms, it's a pair where a larger element comes before a smaller one. The total number of inversions in an array is a direct measure of its sortedness.

  • A completely sorted array has 0 inversions.
  • A completely reverse-sorted array has the maximum number of inversions, which is .

Consider the array [3, 1, 4, 2]. Let's identify the inversions:

  • (3, 1) is an inversion because 3 > 1 and index 0 < index 1.
  • (3, 2) is an inversion because 3 > 2 and index 0 < index 3.
  • (4, 2) is an inversion because 4 > 2 and index 2 < index 3.

This array has a total of 3 inversions. The naive way to count them is to check every possible pair, which requires a double loop and runs in time. For large datasets, this becomes prohibitively slow.

Modifying Merge Sort to Count Inversions

The key insight is that merge sort, a divide-and-conquer algorithm that runs in time, can be augmented to count inversions without worsening its time complexity. Merge sort works by recursively splitting an array in half until it reaches single-element subarrays (which are, by definition, sorted), and then merging these sorted halves back together in order.

The counting happens during the merge step. When merging two sorted halves (left and right), we compare elements from the beginning of each. If the element from the left half is less than or equal to the element from the right half, we simply copy the left element to the output—no inversion exists for this pair. However, if the element from the left half is greater than the element from the right half, we have discovered multiple inversions.

Why multiple? Because both halves are sorted internally. If left[i] > right[j], then left[i] is greater than every element from right[0] up to right[j-1] that has already been placed. More importantly, since left[i] and all remaining elements in the left half (left[i+1], left[i+2], ...) are greater than right[j] (because left is sorted), right[j] forms an inversion with left[i] and all subsequent elements in the left half. Therefore, when this condition is true, we add (mid - i) to the inversion count, where mid is the index separating the two halves.

Step-by-Step: The Merge-Count Procedure

Let's walk through merging two sorted halves while counting inversions. We'll use the array [3, 1, 4, 2]. After the initial recursive splits, we are ready to merge the halves [1, 3] and [2, 4]. Our total inversion count so far (from recursive calls) is 2 (from the inversion (3,1) in the left half). We now need to count cross-inversions during the merge.

We have: left = [1, 3], right = [2, 4] Indices: i=0 (points to 1), j=0 (points to 2), total_inversions = 2.

  1. Compare left[i]=1 and right[j]=2. 1 <= 2. No inversion. Copy 1 to output. i=1.
  2. Compare left[i]=3 and right[j]=2. 3 > 2. Inversion(s) found! Since left from index i (which is 1) to the end has 1 element ([3]), we add (mid - i) = 1 to the count. total_inversions = 2 + 1 = 3. Copy 2 to output. j=1.
  3. Compare left[i]=3 and right[j]=4. 3 <= 4. No inversion. Copy 3 to output. i=2 (end of left).
  4. Copy remaining right element (4) to output.

The merged, sorted array is [1, 2, 3, 4]. The final inversion count is 3, which matches our manual calculation. The recursive nature of merge sort ensures we count inversions within each half and between halves, covering every possible pair.

Complexity and Correctness

The time complexity of this modified algorithm remains , identical to standard merge sort. We only add work during each merge step when we find an inversion. The space complexity is for the auxiliary arrays used during merging.

The proof of correctness hinges on two points. First, the merge procedure correctly counts all inversions where one element is in the left half and the other is in the right half. It does this because when left[i] > right[j], right[j] is the smaller element appearing after all the larger elements in the left half from i onward. Second, the recursive calls correctly count all inversions contained entirely within the left or right halves. By the principle of induction, after merging, the total count represents all inversions in the original array.

Application: Kendall Tau Distance

The inversion count is not just an academic exercise; it has powerful real-world applications. One of the most important is calculating the Kendall tau distance, a metric used to measure the similarity between two rankings or permutations.

The Kendall tau distance between two ranked lists is defined as the number of pairs of items that are in a different order in the two lists. This is directly equivalent to counting the number of inversions needed to transform one ranking into the other.

For example, imagine two search engines rank five websites (A, B, C, D, E):

  • Engine 1: [A, B, C, D, E]
  • Engine 2: [B, A, D, C, E]

To compute their Kendall tau distance, we can use the inversion count. First, take the ranking from Engine 1 as the reference order. Then, create an array that represents the positions of these items in Engine 2's ranking. For item A (position 1 in Engine 1), its position in Engine 2 is 2. For B, it's 1. For C, it's 4. For D, it's 3. For E, it's 5.

This gives us an array: [2, 1, 4, 3, 5]. The number of inversions in this array is the Kendall tau distance between the two rankings. Using our algorithm, we can find it efficiently in time, even for rankings with thousands of items. This makes it practical for analyzing consensus in surveys, comparing recommendation algorithms, or measuring ranking stability.

Common Pitfalls

  1. Miscounting During Merge: The most common error is adding the wrong number of inversions when left[i] > right[j]. You must add the count of remaining elements in the left subarray (mid - i), not just 1. Adding only 1 undercounts, as it misses the inversions between right[j] and the other larger elements still in the left half.
  2. Forgetting to Return the Count: The recursive function must return both the sorted subarray and the inversion count for that subarray. A typical mistake is to sort the array correctly but fail to propagate the inversion counts back up the recursion stack, resulting in a final answer of 0 or an incorrect partial count.
  3. Inefficient Base Case Handling: While not a logical error, ensure your base case (array of size 1) returns an inversion count of 0. Performing any unnecessary comparisons or initializations here can add overhead, though it doesn't change the asymptotic complexity.
  4. Confusing Inversions with Swaps: The inversion count is the minimum number of adjacent swaps needed to sort an array, but the process of counting inversions via merge sort does not simulate those swaps. Do not confuse the algorithmic operation (comparing and merging) with the physical sorting operation (swapping adjacent elements).

Summary

  • An inversion is a pair of elements that are out of order, and counting them measures an array's "unsortedness."
  • By augmenting the merge sort algorithm to add a counter during the merge step, we can count all inversions in an optimal time, a massive improvement over the naive approach.
  • The counting logic is crucial: when an element from the right sorted half is smaller than an element from the left half, it creates an inversion with that left element and all remaining elements in the left half.
  • This inversion count is directly applicable as the Kendall tau distance, a vital statistical measure for comparing the similarity between two ranked lists or permutations.
  • Mastering this algorithm demonstrates a powerful pattern in algorithm design: modifying a well-understood, efficient process (like merge sort) to solve a related problem (counting inversions) without sacrificing performance.

Write better notes with AI

Mindli helps you capture, organize, and master any subject with AI-powered summaries and flashcards.