Solution: Compilation Order

Statement#

There are a total of nn classes labeled with the English alphabet (AA, BB, CC, and so on). Some classes are dependent on other classes for compilation. For example, if class BB extends class AA, then BB has a dependency on AA. Therefore, AA must be compiled before BB.

Given a list of the dependency pairs, find the order in which the classes should be compiled.

Constraints:

  • Class name should be an uppercase character.
  • 00 \leq dependencies.length 676\leq 676
  • dependencies[i].length =2= 2
  • All dependency pairs should be unique.

Solution#

So far, you’ve probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.

Naive approach#

The naive approach is to generate all possible compilation orders for the given classes and then select the ones that satisfy the dependencies.

However, this would be very expensive since there would be an exponential number of possible orders ( n!{n!}, where nn is the number of classes) and only a handful of valid ones. The time complexity for this approach is O(n!)O(n!). The space complexity is O(1)O(1).

Optimized approach using topological sort#

A more optimized solution to the above problem is topological ordering. Topological sort is used to find a linear ordering of elements that have dependencies on or priority over each other. For example, if AA is dependent on BB or if BB has priority over AA, BB is listed before AA in topological order. Since we’re looking for the order of compilation of classes, this problem lends itself naturally to the topological sort pattern.

For this problem, we find the topological order of the given classes using the list of class dependencies. The vertices in the graph represent the classes, and the directed edge represents the dependency relationship.

g A A B B A->B C C A->C E E B->E D D C->D D->E

From the example above, we get the order ABCDEA \rightarrow B \rightarrow C \rightarrow D \rightarrow E. Two other possible orderings of the classes above can be ACBDEA \rightarrow C \rightarrow B \rightarrow D \rightarrow E or ACDBEA \rightarrow C \rightarrow D \rightarrow B \rightarrow E. This order of graph vertices is known as a topological sorted order.

We can say that a topological ordering starts with one of the sources and ends at one of the sinks:

  • Source: Any vertex with no incoming edge and only outgoing edges is called a source.

  • Sink: Any vertex that has only incoming edges and no outgoing edge is called a sink.

The illustration below gives a rundown of the solution:

Created with Fabric.js 3.6.6 A B C E D Maintain a hash map for storing in-degrees, a queue for the sources, and an array to store the topological order. Vertex Indegree Source Topological order

1 of 11

Created with Fabric.js 3.6.6 Vertex Indegree A 0 B 1 C 1 D 1 E 2 Source A Topological order A B C E D Populate the in-degrees and add the source to the queue.

2 of 11

Created with Fabric.js 3.6.6 Vertex Indegree A 0 B 0 C 0 D 1 E 2 Source Topological order A A B C E D Reduce the in-degree of the source's children by 1.Pop the source from the queue and add it to the topological sort array.

3 of 11

Created with Fabric.js 3.6.6 Vertex Indegree A 0 B 0 C 0 D 1 E 2 Source B C Topological order A B C E D Remove the popped vertex from the graph.Vertices with in-degree = 0 become the new sources.Add them to the sources queue.

4 of 11

Created with Fabric.js 3.6.6 B C E D Pop from the source queue and decrement the in-degree of its children by 1.Add the popped source to the topological order array. Vertex Indegree A 0 B 0 C 0 D 1 E 1 Source C Topological order A B

5 of 11

Created with Fabric.js 3.6.6 C E D Remove the previous source from the graph.Vertices with in-degree = 0 are the new sources. Add them to the source queue if not already present. Vertex Indegree A 0 B 0 C 0 D 1 E 1 Source C Topological order A B

6 of 11

Created with Fabric.js 3.6.6 C E D Pop the source from the queue and add it to the topological order array.Reduce the in-degrees of its children by 1. Vertex Indegree A 0 B 0 C 0 D 0 E 1 Source Topological order A B C

7 of 11

Created with Fabric.js 3.6.6 E D Remove the previous source from the graph. Vertices with in-degree =0 become the new source. Add them to the queue. Vertex Indegree A 0 B 0 C 0 D 0 E 1 Source D Topological order A B C

8 of 11

Created with Fabric.js 3.6.6 Vertex Indegree A 0 B 0 C 0 D 0 E 0 Source Topological order A B C D E D Pop from the source queue and add to the topological sort array.Reduce the in-degree of its children by 1.

9 of 11

Created with Fabric.js 3.6.6 Vertex Indegree A 0 B 0 C 0 D 0 E 0 Source E Topological order A B C D Remove the previous source from the graph. Vertices with in-degree = 0 become the new sources.Add them to the source queue. E

10 of 11

Created with Fabric.js 3.6.6 Pop from the source queue and add it to the topological sort array.Remove the vertex from the graph. Since we've visited all the vertices, the algorithm terminates. Vertex Indegree A 0 B 0 C 0 D 0 E 0 Source Topological order A B C D E

11 of 11

Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.

Step-by-step solution construction#

To find the topological sort of a graph, we use a breadth-first search (BFS) approach for traversing the vertices.

We store the graph in adjacency lists in which each parent vertex has a list containing all of its children. For example, in the above slide deck, the adjacency list for vertex AA would be [C,B][C, B]. To find the sources, we maintain a hash map to count the in-degrees, which is the count of incoming edges of a vertex. Any vertex with a 00 in-degree is a source. We store the topological order in a list called sorted order.

Here is how we implement this solution:

  1. We initialize the graph with an empty adjacency list for each vertex. The vertices will have an in-degree of 00. If the length of the graph is 00, that is, there are no vertices, we’ll return an empty list.
Initialising the graph
  1. Next, we build our graph from the input dependencies and populate the in-degrees in the hash map. We traverse over the dependency pairs and identify parent and child classes. Then, we add the child to the parent’s adjacency list and increment it’s in-degree by 1.
Building the graph
  1. The next step is to find all sources, that is, vertices with in-degree =0= 0. These nodes are the first ones to be removed from our graph and added to the sorted order list.
Identifying the sources
  1. We remove the source from the queue and decrement the in-degree of its children by 11. If a child’s in-degree becomes 00, we add it to the source queue. We repeat this step until the source queue is empty and all vertices have been visited.
Updating the in-degrees and removing the sources from the graph

Our algorithm is now complete and returns a topological ordering for the classes. However, it throws an error if there’s a cycle in our dependencies. Therefore, we need to add a condition that caters to this case.

If the length of the sorted order list is not equal to the length of the graph, there’s a cycle and we return an empty list.

Compilation order

Just the code#

Here’s the complete solution to this problem:

Compilation order

Solution summary#

To recap, the solution to this problem can be divided into the following parts:

  1. Build a graph from the dependencies list and keep track of the in-degrees of vertices in a hash map.
  2. Populate the sources deque with vertices with in-degrees =0= 0.
  3. Add the sources to the sorted list.
  4. Remove the sources from the graph and update the in-degrees of their children. If the in-degree of a child becomes 00, it becomes a source in the next iteration.
  5. Repeat until all vertices are visited.

Time complexity#

The time complexity of the above algorithm is O(V+E)O(V+E), where VV is the total number of vertices and EE is the total number of edges in the graph.

Space complexity#

The space complexity is O(V)O(V) since we are creating a deque data structure that will have O(V)O(V) elements in the worst case. We are also maintaining a hash table with the in-degree of the vertices. Its size is O(V)O(V) as well.

Compilation Order

Alien Dictionary