We work with directed Graphs here, which means that the edges are ordered pairs instead of sets here, i.e. ( ist der Vorgänger von , der Nachfolger von ), which means that the edge has a direction, we can travel in, while the other is not allowed.

We define the:

  • In-Degree as the number of edges directed into the vertex .
  • Out-Degree as the number of edges directed out of vertex .

There are special vertices in directed graphs:

  • Source (de: Quelle): a vertex with is called a source.
  • Sink (de: Senke): a vertex with is called a sink.

To represent a directed graph, we can use the same adjacency list or adjacency graph as previously, only that we have a in if exists but not in .

Topological Ordering

In a directed graph, a topological ordering is a linear ordering of the vertices such that for every directed edge , vertex comes before vertex in the ordering. This means that all arrows point rightwards.

Not all graphs have such a topological ordering. The existence of a directed cycle prevents this, as then there is no vertex that comes “first”.

Existence of a topological ordering

There exists a topological ordering There does not exist a directed Closed Walk.

A directed graph has a topological ordering if and only if it’s a Directed Acyclic Graph (DAG), meaning it contains no cycles.

Topological Sort Algorithm

The core idea of the topo-sort algorithm is that it repeatedly finds and locates sinks and removes them from the graph. We can then put the sink at the last place in our list, remove it and solve the rest recursively.

Visit function

We define the visit function:

def visit(u, topological_order):
   u.mark()
   for v in u.successors():
   	if not v.isMarked():
   		visit(v, topological_order)
   topological_order.insert(0, u)   # Prepend u to maintain order

Visit processes all vertices reachable from before adding to the order, meaning that all vertices depending on come first. This is what we require for topo-sort.

To show that this approach works, we need to prove the following:

If there is no closed walk, a sink exists

If there is no closed walk (Zyklus) in exists a Sink

Proof There is a path ending in , found by our path algorithm - this means that has no more unmarked successors. If we assume for contradiction that is not a sink, then there exists a marked successor , which means there is a cycle - contradiction.

We have the following function that performs DFS and finds a topological ordering:

def dfs_topological_sort(G):
	G.unmarkAll()
	topological_order = []
	for u in G.nodes():
		if not u.isMarked():
			visit(u, topological_order)
	return topological_order

has the following recursion tree when we run DFS on it.

Deeper understanding using pre- and postordering

We can augment the DFS implementation by also tracking in what order which edges and vertices were visited:

def visit(u, topological_order, pre, post, T):
    pre[u] = T
    T += 1
    u.mark()
    for v in u.successors():
        if not v.isMarked():
            T = visit(v, topological_order, pre, post, T) # T is modified recursively.
    topological_order.insert(0, u)
    post[u] = T
    T += 1
    return T

Note: DFS can be implemented efficiently using a stack, similar to how BFS uses a queue.

This gives us the following Graph:

We can then define an Intervall . We then have a nesting of the different intervals, which is a different way to draw the recursion tree:

We can then classify the edges based on the interval patterns of the connected vertices:

DFS Edge Classification

  1. Tree Edge: If , then is a tree edge. This means is a descendant of in the DFS tree. The visit(u) call directly leads to the visit(v) call.
    *Example: ,

  2. Back Edge: If , then is a back edge. This indicates a cycle in the graph. is a descendant of , and the edge goes back to an ancestor.
    *Examples: ,

  3. Forward Edge: If , then is a forward edge. This means is a descendant of , but the edge is not part of the DFS tree. There is a path from to in the tree but a direct edge as well.
    Examples: ,

  4. Cross Edge: If , then is a cross edge. This means and are in different branches of the DFS tree, and there is no ancestor-descendant relationship between them. They belong to different subtrees that don’t overlap.
    Examples: ,

Impossible Cases:

  • Overlapping but not Nested Intervals:
    • Due to the recursive nature of DFS, two intervals cannot partially overlap without one completely containing the other.
  • :
    • This case is impossible because visit(u) would call visit(v) if v was not yet marked, so the interval of v can’t end before u.

Observations

(1)

back-edge directed closed walk

(2)

not back-edge ,

We can conclude from this that if directed closed walk that back-edge reversed post-order (our DFS output) is topological sorting.

Topological Sorting from Post-Order

The reversed post-order is a topological order.

DFS in undirected Graphs

In an undirected Graphs, we have two more properties that hold:

  1. back-edges = forward-edges
  2. cross edges are not possible

Find cycles using DFS

If during the execution of DFS, we add the additional check of checking all adjacent nodes, we can detect cycles.

If we find an adjacent vertex without a post-number, this means there’s a back-edge. If we find one with both pre- and post-number, there’s a cross-edge.

Bipartite Check with DFS

Same as with BFS, maintain a colouring of all vertices. If during traversal, there’s an edge to a vertex with the same colour, there’s an odd cycle and we return false.

Runtime Analysis of Topological Sorting

Adjacency Matrix

  • visit is called times (one call per vertex)
  • visit(u) takes + time in recursive calls Total: Thus DFS has a runtime of with the adjacency list.

Adjacency List

  • visit(u) takes + time in recursive calls Total: Thus DFS has a runtime of with the adjacency list.

This means that in sparse graphs the adjacency list performs better, while in densely connected graphs where , we have a better performance with the adjacency matrix.

Specifically, if , then DFS has the same runtime in the worst-case using adjacency matrices or lists.