12.6 Complexity analysis

Graph algorithms have potential to be very inefficient if designed carelessly. Consider this naive approach to the shortest path problem from vertex a to b: Try every possible path between a and a, and keep the shortest one. Since a (simple) path is a sequence of edges a\rightarrow\cdots\rightarrow b, every permutation of the vertices forms a potential path (in a complete graph). Even a conservative worst case estimate gives us O(V!) such permutations for a graph with V vertices. This makes the algorithm too slow for any practical application.

For the algorithms we have presented, here is a slightly simplified summary:

Let us look at the reasononing for this, and try to sort out some caveats. All our graph traversal algorithms (DFS, BFS, Dijkstra’s, Prim’s) are on the form:

We can make the following observations:

In online sources, you often find different answers to the complexity of Dijkstra’s algorithm. This is primarily due to

If the visitation set is an efficient hash table implementation, and vertices have a very good hash function, then initialisation, lookup and adding to the set all take amortised constant time. With these assumptions, the set operations can largely be ignored. In general, the number of edges E will be somewhere between 0 and V^2. If the graph is assumed to be connected (very common for traversal algorithms), then E \geq V-1. If the graph is assumed to be sparse, then E \in O(V). So if the graph is both connected and sparse, V and E are interchangeable. Furthermore, you sometimes see \log(V) and sometimes \log(E). These are in fact interchangeable for all connected graphs, since E \leq V^2 and \log(V^2) \in O(\log V).

With all these caveats in mind, we will look at the algorithms assuming graphs are connected, and that all data structure operations except those on the agenda are O(1). For DFS and BFS, we process every edge by adding them to a stack and queue respectively, so the operations on the agenda are O(1), giving a total complexity of O(E) for both these algorithms.

For Dijkstra’s and Prim’s, the agenda is a priority queue. Adding to and removing from a binary heap is logarithmic in its size, and the size of the agenda is at most E. This gives a total time of O(E\log(E)) for these algorithms, which is the same as O(E\log(V)).

For Kruskal’s algorithm, if we use union-find to detect cycles, the time will be dominated by sorting the edges by weight. This is O(E\log(E)) for an efficient sorting algorithm, giving the algorithm the same time complexity as Prim’s algorithm.

Example: Airlines connecting N airports

We have N airports, and know the flight time between all pairs of airports. We want to find a set of direct flights connecting all airports, with as low total flight time as possible. What is the time complexity of this task, in terms of the number of airports N?

This is an MST problem on a complete graph with N vertices (so V=N). Prim’s and Kruskal’s are both O(E\log(E)), but since the graph is complete, O(E)=O(V^2)=O(N^2). So the complexity is O(N^2\log(N^2)) which can be simplified to O(N^2\log(N)). A common mistake here would be answering simply O(E\log(E)), but the question specifically asks for the complexity in terms of the number of airports.