- Graph: A set of vertices (a.k.a. nodes) connected pairwise by edges.
- Vertices with an edge beteween them are adjacent.
- Path is a sequence of vertices connected by edges.
- A cycle is a path is path whose first and last vertices are the same.
- Vertices are connected if there is a path between them.
Interesting Graph Problems.
- Is there a path between s and t?
- Is there a cycle in the graph?
- Is a graph bipartite?
- Euler cycle. Is there a cycle that uses every edge exactly once?
- Hamiltonian cycle. Is there a cycle that uses every vertex exactly once?
- Planarity problem. Can the graph be drawn in the plane with no crossing edges? (See this online game for a fun (?) look at the problem.)
- Graph isomorphism. Are two graphs the same graph (but with different vertex names)
Graph Representation. There are many possible internal representations of a graph. We discussed three. You should be able to determine the runtime tradeoffs for each.
- List of edges
- Adjacency matrix
- Adjacency lists
Usually, we use the adjacency-lists representation because most real-world graphs are sparse.
Depth First Traversal. Much like we can traverse trees, we can also traverse graphs. The most natural traversal is the so called depth first traversal, represented by the following pseudocode:
for each unmarked neighbor X of s:
The result is that we explore the entirety of each adjacent subgraph before moving on to the next subgraph. DFS is the same thing as our depth first traversals for trees (a.k.a. preorder, inorder, and postorder). In the next lecture we'll differentiate DFS preorder vs. DFS postorder.
Paths: A Case Study. We use the Paths problem to showcase two things: decoupling and the power of DFS.
By decoupling, I mean the fact that graph algorithms are not usually included inside a Graph class itself. This is because there are just too many such algorithms that are interesting, which result in a very bloated Graph class. Instead, we usually create an object whose constructor takes a Graph object, and it does all the processing at construction time.
In the Paths problem, we create a class that supports hasPathTo() in constant time and pathTo() in time proportional to the length of the path.
We create a solution class DepthFirstPaths whose constructor is just DFS as described above, but where we also set the edgeTo for each vertex before we visit it. We did not discuss how to write hasPathTo() or pathTo(), but it should be clear how we'd achieve this goal.
Throughout these problems, we define V as the number of vertices in a graph, and E as the number of edges.
- Why is the runtime of DFS O(V + E) time if we use an adjacency list?
- What is the runtime of DFS if we use an adjacency matrix? Give the tightest big-O bound you can.
- Adapted from Algorithm 4.10: Prove that every connected graph has a vertex whose removal (including all adjacent edges) will not disconnect the graph, and create a DFS based algorithm that finds such a vertex.
- Just for fun: Prove that a graph is two-colorable (bipartite) if and only if it contains no odd-length cycle.