The Algorithm Application: Topological Sorting

J.E.D.I 0,1 0,3 0,5 1,2 1,5 2,4 3,2 3,4 5,4 6,5 6,7 7,1 7,5 Figure 1.20 Example for Topological Sorting 0 6 3 7 1 2 5 4

3.5.1 The Algorithm

In coming up with the implementation of the algorithm, we must consider some components as discussed in chapter 1 – input, output and the algorithm proper. • Input. A set of number pairs of the form i, j for each relation i ≼ j could represent the partial ordering of elements. The input pairs could be in any order. • Output. The algorithm will come up with a linear sequence of items such that no item appears in the sequence before its direct predecessor. • Algorithm Proper. A requirement in topological sorting is not to output the items with which the predecessors are not yet in the output. To do this, there is a need to keep track of the number of predecessors for every item. A vector could be used for this purpose. Lets call this vector COUNT. When an item is placed in the output, the count of every successor of the item is decremented. If the count of an item is zero, or it becomes zero as a result of putting in the output all of its predecessors, that would be the time it is considered ready for output. To keep track of the successors, a linked list named SUC, with structure INFO, LINK, will be used. INFO contains the label of the direct successor while LINK points to the next successor, if any. The following shows the definition of the Node: class Node{ int info; Node link; } The COUNT vector is initially set to 0 and the SUC vector to NULL. For every input pair i, j, COUNT[j]++; Data Structures 46 J.E.D.I and a new node is added to SUCi: Node newNode = new Node; newNode.info = j; newNode.link = SUC[i]; SUC[i] = newNode; Figure 1.21 Adding a New Node Figure 1.22 The following is an example: Figure 1.23 Representation of the Example in Topological Sorting Data Structures 47 J.E.D.I Figure 1.24 Figure 1.25 To generate the output, which is a linear ordering of the objects such that no object appears in the sequence before its direct predecessors, we proceed as follows: 1. Look for an item, say k, with count of direct predecessors equal to zero, i.e., COUNT[k] == 0. Put k in the output. 2. Scan list of direct successors of k, and decrement the count of each such successor by 1. 3. Repeat steps 1 and 2 until all items are in the output. To avoid having to go through the COUNT vector repeatedly as we look for objects with a count of zero, we will constitute all such objects into a linked queue. Initially, the queue will consist of items with no direct predecessors there will always be at least one such item. Subsequently, each time that the count of direct predecessors of an item drops to zero, it is inserted into the queue, ready for output. Since for each item, say j, in the queue, the count is zero, we can now reuse COUNT[j] as a link field such that COUNT[j] = k if k is the next item in the queue = 0 if j is the rear element in the queue Hence, we have an embedded linked queue in a sequential vector. If the input to the algorithm is correct, i.e., if the input relations satisfy partial ordering, then the algorithm terminates when the queue is empty with all n objects placed in the output. If, on the other hand, partial ordering is violated such that there are objects which constitute one or more loops for instance, 1 ≺ 2; 2 ≺ 3; 3 ≺ 4; 4 ≺ 1 , then the algorithm still terminates, but objects comprising a loop will not be placed in the output. This approach of topological sorting uses both sequential and linked allocation techniques, and the use of a linked queue that is embedded in a sequential vector.

3.6 Summary