1. Overview

In this tutorial, we’ll discuss the problem of counting inversions in an array. Firstly, we’ll define the problem and provide an example that explains the meaning of an inversion.

Secondly, we’ll discuss two approaches to solving the problem and talk about the good usage of the problem.

2. Defining The Problem

Suppose we have an array A of n integers, which are enumerated from 0 to n - 1. The value of the i^{th} element is equal to A_i.

An inversion is a pair of indices (i, j) that satisfies the following:

    \[\boldsymbol{A_i > A_j ; 0 \leq i < j \leq n - 1}\]

In other words, an inversion is a pair of two distinct indices such that the order of these two indices is the reverse order of their values.

The problem gives us an array and asks to compute the number of inversions in this array.

Let’s check the example for better understanding:

Suppose we have an array A = [1, 2, 3, 1, 2]. The answer in this case is 3 because we have (1, 3), (2, 3) and (2, 4) as inversions.

Note that when the array is sorted increasingly, the number of inversions is equal to 0. On the other hand, when the array is sorted decreasingly, the number of inversions is the maximum possible over all the permutations of the array (it will be equal to (n \times (n - 1)) / 2 when elements are pairwise distinct).

In other words, the number of inversions refers to how far the array is from being sorted.

3. Naive Approach

3.1 Main Idea

The idea is straightforward. We perform a brute force by iterating over all pairs of indices and check whether this pair satisfies the condition mentioned in section 2.

3.2 Implementation

Let’s Take a look at the implementation:

Rendered by QuickLaTeX.com

Firstly, we initialize answer with zero, which will hold the resulting answer. Then, we start by iterating over the first element i of the pair. Then, we iterate over the second element j. Note that since we start j from i + 1, we guarantee that j > i.

Next, we check the pair (i, j) to see whether it satisfies the inversions condition or not. If yes then we have a new inversion, and we increase the total number of inversions by one.

Finally, we return the resulting answer.

The complexity of the naive approach is \boldsymbol{O(n^{2})}, where n is the length of A.

4. Divide-And-Conquer Approach

4.1 Theoretical Idea

Suppose we want to count the number of inversions inside the array A. Let’s divide it into two equal Arrays and call the first one L and the second one R. Now an inversion \boldsymbol{(i, j)} can have one of the following types:

  1. \boldsymbol{i \in L} and \boldsymbol{j \in R}.
  2. \boldsymbol{i \in L} and \boldsymbol{j \in L}.
  3. \boldsymbol{i \in R} and \boldsymbol{j \in R}.

Consider the first type. The problem turns out to computing for each index i \in L, the number of indices j \in R such that L_i > R_j.

Now suppose that both arrays \boldsymbol{L} and \boldsymbol{R} are sorted increasingly, then we can use the two-pointers technique to solve it. That’s because indices j that satisfy the condition for an index i always form a prefix from the array R, when i gets bigger, the length of the prefix increases.

But, what about the second and third types? The answer is simple: just perform the same algorithm recursively for both L and R independently.

Back to the first type, we supposed that both L and R are sorted increasingly. To achieve that, the algorithm must sort them. In other words, after applying the algorithm to both L and R, they become sorted. To sort \boldsymbol{A} we just merge the two sorted arrays \boldsymbol{L} and \boldsymbol{R} into \boldsymbol{A}.

4.2 Implementation

Let’s start by implementing a function that counts the number of inversions of the first type:

Rendered by QuickLaTeX.com

Firstly, we initialize answer with zero, which will hold the resulting answer. Then, we initialize j with zero, which is the first index in the R that breaks the condition for the current i. More formally, it’s the first index such that \boldsymbol{L_i \leq R_j}. Since we’re using zero-indexed arrays, \boldsymbol{j} represents the length of the prefix from \boldsymbol{R} that satisfies the condition.

While we increase i, we must increase j as well. This continues until it breaks the condition again.

After computing the correct j, we add it to the answer as the number of elements in R less than L_i.

Finally, we return the resulting answer.

Now, let’s take a look at the implementation of the complete algorithm:

Rendered by QuickLaTeX.com

Firstly, we check whether the length of the array is less than or equal to 1. If so, then the number of inversions is equal to 0.

Otherwise, we get L and R as the first and second halves of the array using the function subArray that gets the subarray from i (inclusive) to j (exclusive).

Next, We add the number of inversions in L and R by calling CountInv recursively on both halves. Since both \boldsymbol{L} and \boldsymbol{R} become sorted, we can compute the number of inversions of the first type using the \boldsymbol{CountInvOfFirstType} function.

After that, we merge the two sorted arrays into the array A using merge function so that it becomes sorted as well. We assume that both functions countInv and merge take a reference to the array A rather than taking a copy. Thus, when it gets edited inside the function, the edit will affect the original array.

4.3 Complexity

We note that the previous algorithm is very similar to the merge sort algorithm. The only difference is that we make another iteration on the elements of the array A when we count the number of inversions of the first type (the first iteration was when we merged the two subarrays).

Therefore, the complexity is the same as the merge sort algorithm, which is \boldsymbol{O(n \times log(n))}.

5. Usage

Suppose we have an array A, and we want to find the minimum number of operations to get the array sorted. In one operation, we can swap any two adjacent elements.

The answer is the number of inversions. That’s because, in each operation, we decrease the number of inversions by \boldsymbol{1}. When the array is sorted, the number of inversions will be equal to 0.

6. Conclusion

In this tutorial, we presented the problem of counting the number of inversions in an array. We explained the general idea and discussed two approaches for solving it. Also, we provided a fair usage of the discussed problem.

guest
0 Comments
Inline Feedbacks
View all comments