Author: Hayden Smith 2021
Why?
What?
A graph has a cycle if, at any point in the graph:
This graph has 3 distinct cycles: 0-1-2-0, 2-3-0-2, 0-1-2-3-0
This graph has MANY cycles: 0-4-3-0, 2-4-5-2, 0-1-2-5-4-6-3-0, etc...
Cycle checking involves two stages: The actual checking a cycle between two vertices, and the looping over all possible vertex combinations.
Implementation in C:
Vertex *visited;
bool hasCycle(Graph g, Vertex s) {
bool result = false;
visited = calloc(g->nV,sizeof(int));
for (int v = 0; v < g->nV; v++) {
for (int i = 0; i < g->nV; i++)
visited[i] = -1;
if (dfsCycleCheck(g, v, v)) {
result = true;
break;
}
}
free(visited);
return result;
}
bool dfsCycleCheck(Graph g, Vertex v, Vertex u) {
visited[v] = true;
for (Vertex w = 0; w < g->nV; w++) {
if (adjacent(g, v, w)) {
if (!visited[w]) {
if (dfsCycleCheck(g, w, v))
return true;
}
else if (w != u)
return true;
}
}
return false;
}
For a given graph (which may be disjoint), we want to find out: How many connected subgraphs are there?
Easiest solution: Create a componentOf[] array, where each index refers to a vertex, and the value at that index refers to which graph it's in.
For a given graph (which may be disjoint), we want to find out: How many connected subgraphs are there?
Easiest solution: Create a componentOf[] array, where each index refers to a vertex, and the value at that index refers to which graph it's in.
components(G):
for all vertices v ∈ G:
componentOf[v] = -1
compID = 0 // component ID
for all vertices v ∈ G:
if componentOf[v] == -1:
dfsComponent(G, v, compID)
compID = compID + 1
dfsComponent(G, v, id):
componentOf[v] = id
for each (v,w) ∈ edges(G):
if componentOf[w] == -1:
dfsComponent(G, w, id)For applications where connectivity is critical, we can't afford to run components() each time. But we can cache some information by adding information to our graph implementation struct. Adding or removing of edges may increase or decrease nC.
Another speedup: Ensure v, w are in the same component before starting a search
typedef struct GraphRep *Graph;
struct GraphRep {
...
int nC; // # connected components
int *cc; // which component each vertex is contained in
... // i.e. array [0..nV-1] of 0..nC-1
}Hamiltonian path problem: A path connecting two vertices (v, w) in graph G such that the path includes each vertex exactly once.
Hamiltonian Circuit: A Hamiltonian path where the starting vertex = ending vertex
Real world applications include:
Approach:
Expressed via a recursive DFS algorithm. Very similar to path finding, except we track path lengths.
hasHamiltonianPath(G,src,dest):
visited[] // keep track of visited vertices
for all vertices v ∈ G:
visited[v] = false
return hamiltonR(G, src, dest, #vertices(G) - 1)
hamiltonR(G,v,dest,d):
if v = dest:
return d == 0
else:
visited[v] = true
for each (v,w) ∈ edges(G) where not visited[w]:
if hamiltonR(G, w, dest, d - 1):
return true
visited[v] = false // reset visited mark
return falseWorst case: O((V - 1)!)
Euler path problem: Find a path connecting two vertices (v, w) in graph G such that the path includes each edge exactly once.
Euler Circuit: A Euler path where the starting vertex = ending vertex
Real world applications include:
Origins are from Leonhard Euler who was trying to find a way to cross all the bridges of Konigsberg exactly once on a walk through the town.
A brute force (do many checks, would be O(n!)) would be to find every possible path and check if it's a Euler path. But we have a shortcut:
1. A graph has a Euler circuit if and only if it is connected and all vertices have even degree
2. A graph has a Euler path (non-circuit) if and only if it is connected and exactly two vertices had odd degree
hasEulerPath(G, src, dest):
if src != dest:
if degree(G, src) is even and degree(G,dest) is even:
return false
for all vertices v ∈ G:
if v != src and v != dest and degree(G,v) is odd:
return false
return trueIf we assume connectivity is already checked: