Breadth First Search (BFS)
Given a graph
vector<int> dist;
vector<int> parent;
/**
* Traverses a graph `G` of order `n` and size `m` by breadth
*
* Time complexity: O(n + m)
* Space complexity: O(n)
*
* @param {vector<vector<int> >} g The adjacency list representation
* of a graph
* @param {int} source The source vertex
*/
void bfs(vector<vector<int> > &g, int source) {
int n = g.size();
dist.assign(n, -1);
parent.assign(n, -1);
queue<int> remaining;
dist[source] = 0;
remaining.push(source);
while (!remaining.empty()) {
int current = remaining.front();
remaining.pop();
for (int i = 0; i < g[current].size(); i += 1) {
int next = g[current][i];
if (dist[next] == -1) {
dist[next] = dist[current] + 1;
parent[next] = current;
remaining.push(next);
}
}
}
}
Depth First Search (DFS)
Given a graph
Whenever a vertex
During the process of creation of the dfs tree the algorithm can also define timestamps on each vertex (an integer denoting the time an action happened)
recorded when is first discovered recorded when the search finishes exploring ’s adjacent vertices
Properties
- the number of descendent of any vertex
is equal to - for any two vertices
and exactly one of the following holds - if the interval
and are disjoint intervals then neither is a descendant of nor a descendant of in the dfs tree - if the interval
is contained in then is a descendant of - if the interval
is contained in then is a descendant of
Classification of edges
We can define four edge types produced by a DFS on
- Tree edges, an edge
is a tree edge if was first discovered by - Back edges, an edge
is a back edge if it connects with an antecesor of of ) - Forward edges, an edge
is a forward edge if it connects with a descendant of (nontree edge) - Cross edges, all the other edges, e.g. an edge between branches in the dfs tree
We can identify these edges with an additional state stored in the vertices of the graph during the dfs tree process, the additional state will be
if a vertex wasn’t explored yet when a vertex is discovered first when a vertex has finished exploring its adjacent vertices
During the analysis of an edge we can take a look at the color of the adjacent vertex to determine the type of edge, given the edge
- if
then is a tree edge - if
then is a back edge - if
then is a forward/cross edge
Another way to determine the type of edge is by analyzing the states
- if
is not defined then is a tree edge - if
is defined and is not defined then is a back edge - if
is defined and is defined and then is a forward edge - if
is defined and is defined and then is a cross edge
Additional properties of the edges
- if
is an undirected graph then every edge of is either a tree edge or a back edge during the exploration of the dfs tree - a directed graph
is acyclic if it contains no back edges
int time_spent = 0;
// the adjacency list of `G`
vector<vector<int> > g;
// the explored state of a vertex `i`
vector<bool> visited;
// the predecesor of a vertex `i` in the dfs tree
vector<bool> predecessor;
// the time a vertex `i` was discovered first
vector<int> time_in;
// the time a vertex `i` spent exploring each reachable non-visited vertices
vector<int> time_out;
/**
* Traverses a graph `G` of order `n` and size `m` by depth,
* it's assumed that `time_in`, `time_out`, `visited`, `predecessor`
* are initialized correctly with a size equal to `n`
*
* Time complexity: O(n + m)
* Space complexity: O(n)
*
* @param {int} v The current vertex being analyzed
*/
void dfs(int v) {
visited[v] = true;
time_in[v] = ++time_spent;
for (int i = 0; i < g[v].size(); i += 1) {
int next = g[v][i];
// edge analysis
if (!time_in[next]) {
// edge (v, next) is a tree edge
} else if (!time_out[next]) {
// edge (v, next) is a back edge
} else if (time_in[v] < time_in[next]) {
// edge (v, next) is a forward edge
} else {
// edge (v, next) is a forward edge
}
// traversal to adjacent vertices
if (!visited[next]) {
predecessor[next] = v;
dfs(next);
}
}
time_out[v] = ++time_spent;
}