In this chapter, we study two representations of graphs and basic algorithms on these representations.
Mathematically, a (directed) graph is a pair where is a set of vertices and is a set of ordered pairs of vertices called edges. An edge is directed from to ; is called the source of the edge and is called the target. A path in is a sequence of vertices such that, for every , the edge is in . A path is a cycle if, additionally, the edge is in . A path (or cycle) is simple if all of its vertices are unique. If there is a path from some vertex to some vertex then we say that is reachable from . An example of a graph is shown in Figure 12.1.
Graphs have an enormous number of applications, due to their ability to model so many phenomena. There are many obvious examples. Computer networks can be modelled as graphs, with vertices corresponding to computers and edges corresponding to (directed) communication links between those computers. Street networks can be modelled as graphs, with vertices representing intersections and edges representing streets joining consecutive intersections.
Less obvious examples occur as soon as we realize that graphs can model any pairwise relationships within a set. For example, in a university setting we might have a timetable conflict graph whose vertices represent courses offered in the university and in which the edge is present if and only if there is at least one student that is taking both class and class . Thus, an edge indicates that the exam for class can not be scheduled at the same time as the exam for class .
Throughout this section, we will use to denote the number of vertices of and to denote the number of edges of . That is, and . Furthermore, we will assume that . Any other data that we would like to associate with the elements of can be stored in an array of length .
Some typical operations performed on graphs are:
Note that these operations are not terribly difficult to implement efficiently. For example, the first three operations can be implemented directly by using a , so they can be implemented in constant expected time using the hash tables discussed in Chapter 5. The last two operations can be implemented in constant time by storing, for each vertex, a list of its adjacent vertices.
However, different applications of graphs have different performance requirements for these operations and, ideally, we can use the simplest implementation that satisfies all the application's requirements. For this reason, we discuss two broad categories of graph representations.