Approaches To Problem Solving #5( Array | Graph | Components)
By: Ankesh Krishna Prasad
Tags: Knowledge Sharing, Share Learnings, Problem Solving, Sport Programming

Photo by Markus Spiske on Unsplash

In our previous blog, we already started with the introduction to one of the problems that we are going to discuss. We will try to discuss further about the topics involved.

Let us revisit the problem statement,

Problem #1: Given an array of N integers, find out the minimum number of swaps (not necessarily adjacent) required to sort the array in ascending order.

This problem is the advanced version of the “Counting Inversions in an array”, which we discussed in our previous blog. Special Credits to Gaurav Tiwari, for reminding us about this problem.

Let us consider an example before we try to solve this problem,

Unsorted Input Array

One important point to note here is that it is clearly mentioned that we have to sort the given array in ascending order. Sometimes, for example, in an interview, one might not be given explicit details about the order in which we have to sort the array. Hence, it is essential that one is clear about the final ordering (decreasing or increasing)that we want to achieve.

The final state of the array in this problem should look like the following,

Sorted Array

First intuition should be to consider only those elements in the input array which are not at their respective final positions in the sorted array. Here, the elements which need to be considered are, 2, 3, 5, 6 and 7, since 1 and 4 are the only elements present at their correct final positions in the input array.

Let us now try to visualise where these elements should go to, in order for us to achieve the final state of the array,

Considering the tail and head of the red indicators, the tail depicts the initial position and the arrow depicts the final position of the ith element.

We can clearly observe that the red indicators used for elements 2 and 3 are completely independent of the indicators used for elements 5, 6, and 7. Also, elements 2 and 3 are interdependent and indicators pointing to 5, 6 and 7 are also dependent upon each other. Thus, an initial hunch should be that if we could find solution for each independent set of indicators then it would really help us to ultimately find the final solution. If we had to apply the same case for this given example, we could make two sets ({2, 3} and {5, 6, 7}). If we can find a solution for each set independently then we know that by adding them up, we would find the final solution.

Phew! We have reached somewhat closer to the final solution but what to do with these sets that we have? How do we find the solution for each set?

It is time for some interesting observations. Note that, all such independent sets that we make will have all their elements “not” at their correct final positions. It is easy to prove the above statement. How? If some element i belongs to some set S, then the fact that it belongs to some set S, itself makes it clear that the ith element was not in its final sorted position. Why? Remember that we only considered those elements which were not on their correct final positions otherwise they can’t be a part of some set.

With that proof with us, we can integrate the other fact with our current idea, to build our final solution. What is that “other fact”? The “other fact” mentioned here is that in order to sort a completely unsorted array the minimum number of swaps required is always equal to N — 1, where N is the size of the array.

In this example, the size of the first set({2, 3}) is 2 and the size of the second set({5, 6, 7}) is 3. Hence, the first set can be sorted in exactly 2–1 = 1 moves and the second set can be sorted in exactly 3–1 = 2 moves. Thus, our final answer is 1 + 2 = 3(swap 2 and 3, swap 5 and 7, swap 5 and 6).

Approach #1: Looking at the indicators in the image above, one hint that we get is that we can convert this array into a graph. How? Consider each and every index in the array as a node. We draw edges between two nodes i and j only when i depicts the initial position for some element and j depicts the final position of the same element. Following this, our newly built graph would look something like this,

Newly constructed graph

If we have successfully reached this far, then applying the concept of counting the size of each connected component seems pretty obvious next thing to do. We can start Depth-First Search(DFS) for each unvisited node and keep marking nodes visited as we find them. By doing this, we can maintain the size of the connected component for each node from which we could start our DFS. Adding them all up in some global variable for example, we can find our final answer. The time complexity of this solution is ~O(V + E), where V is the total number of nodes and E is the number of edges that the built graph has.

Tada!! we have reached the solution.

Although using DFS for finding connected components is easier to think about and implement, one could also get hints for reaching the solution using Disjoint Set Union which is completely alright.

We can try to solve the following problems to have a better understanding of the above approach :-