All the facts/properties below are considered for an undirected connected graph
- if
is a vertex incident with a bridge in a graph then is a cut-vertex if (if then is an end-vertex of so is still connected) - given that the order
is , if it contains a bridge then it also contains a cut-vertex - if
is a cut-vertex of and , are vertices in different components formed by then is part of every path in - let
, if is a vertex that is farthest from then is not a cut-vertex
Let
- a leaf vertex is not an cut-vertex
- let
and be two vertices of the dfs such that is an antecesor of- if
and are not adjacent and there’s a back edge to some vertex such that is an predecessor of then none of the vertices in the path are cut-vertices - let
and are not adjacent and there’s a back edge from to some vertex in the path then is a cut-vertex
- if
- let
be the root node of the dfs tree, it’s an cut-vertex if during the exploration of its successor vertices finds out that it has more than one children i.e. the root has more than one branch in the dfs tree
int time_spent;
// the adjacency list representation of `G`
vector<vector<int> > g;
// the time a vertex `i` was discovered first
vector<int> time_in;
// stores the discovery time of the lowest predecessor that vertex `i`'s
// succesor vertices can reach **through a back edge**, initially
// the lowest predecessor is set to the vertex itself
vector<int> back;
// the articulation points found during the dfs
vector<int> cut_vertex;
void dfs(int v, int parent) {
// the lowest back edge discovery time of `v` is
// set to the discovery time of `v` initally
back[v] = time_in[v] = ++time_spent;
// count the number of children for the `root` vertex
int children = 0;
int is_cut_vertex = false;
for (int i = 0; i < g[v].size(); i += 1) {
int next = g[v][i];
if (next == parent) {
continue;
}
if (time_in[next] == -1) {
dfs(next, v);
/// if there's a back edge between a descendant of `next` and
// a predecessor of `v` then `next` will have a lower reachable
// vertex than `v` through a back edge, in this case the vertex `v` is not
// a cut-vertex (the special case of the root node is handled below)
if (back[next] >= time_in[v] && parent != -1) {
is_cut_vertex = true;
}
// propagation of the back edge to a vertex with the lowest discovery time
back[v] = min(back[v], back[next]);
++children;
} else {
// * back edge *
// update index of the vertex incident with this back edge to
// be the one with the lowest discovery time
// it's possible for this edge to be a *forward edge*, in that
// case the time won't be updated since time[v] < time[next]
back[v] = min(back[v], time_in[next]);
}
}
// the root vertex of the dfs tree is a cut-vertex
// if it has more than two children in the dfs tree
if (parent == -1 && children > 1) {
is_cut_vertex = true;
}
if (is_cut_vertex) {
cut_vertex.push_back(v);
}
}
/**
* Finds the articulation points in an undirected graph `G`
* of order`n` and size `m`
*
* Time complexity: O(n + m)
* Space complexity: O(n)
*
* @returns {int} the number of articulation points
*/
int articulation_points() {
int n = g.size();
time_spent = 0;
time_in.assign(n, -1);
back.assign(n, -1);
cut_vertex.clear();
for (int i = 0; i < n; i += 1) {
if (time_in[i] == -1) {
dfs(i, -1);
}
}
return cut_vertex.size();
}
Biconnected components in an undirected graph
A biconnected graph is a nonseparable graph meaning that if any vertex is removed the graph is still connected and therefore it doesn’t have cut-vertices
Key observations:
- two different biconnected components can’t have a common edge (but they might share a common vertex)
- a common vertex linking multiple biconnected components must be a cut-vertex of
Let
int time_spent;
// the adjacency list representation of `G`
vector<vector<int> > g;
// the time a vertex `i` was discovered first
vector<int> time_in;
// stores the discovery time of the lowest predecessor that vertex `i`'s
// succesor vertices can reach **through a back edge**, initially
// the lowest predecessor is set to the vertex itself
vector<int> back;
// the biconnected components found during the dfs
vector<vector<pair<int, int> > > bcc;
stack<pair<int, int> > edges_processed;
void output_biconnected_component(int u, int v) {
pair<int, int> top;
vector<pair<int, int> > component;
do {
top = edges_processed.top();
edges_processed.pop();
component.push_back(top);
} while (u != top.first || v != top.second);
bcc.push_back(component);
}
void dfs(int v, int parent) {
// the lowest back edge discovery time of `v` is
// set to the discovery time of `v` initally
back[v] = time_in[v] = ++time_spent;
// count the number of children for the `root` vertex
int is_cut_vertex = false;
for (int i = 0; i < g[v].size(); i += 1) {
int next = g[v][i];
if (parent == next) {
continue;
}
// mark the edge (v, next) as processed
if (time_in[next] == -1) {
// this edge is being processed right now
edges_processed.push(pair<int, int> (v, next));
dfs(next, v);
// if there's a back edge between a descendant of `next` and
// a predecessor of `v` then `next` will have a lower reachable
// vertex than `v` through a back edge, in this case the vertex `v` is not
// a cut-vertex
if (back[next] >= time_in[v]) {
output_biconnected_component(v, next);
}
// propagation of the back edge to a vertex with the lowest discovery time
back[v] = min(back[v], back[next]);
} else if (time_in[next] < time_in[v]) {
// * back edge *
// update index of the vertex incident with this back edge to
// be the one with the lowest discovery time
back[v] = min(back[v], time_in[next]);
// push this edge to the stack only once
edges_processed.push(pair<int, int> (v, next));
}
}
}
/**
* Finds the biconnected components in an undirected graph `G`
* of order`n` and size `m`
*
* Time complexity: O(n + m)
* Space complexity: O(m)
*
* @returns {int} the number of biconnected components
*/
int biconnected_components() {
int n = g.size();
time_spent = 0;
time_in.assign(n, -1);
back.assign(n, -1);
while (!edges_processed.empty()) {
edges_processed.pop();
}
bcc.clear();
for (int i = 0; i < n; i += 1) {
if (time_in[i] == -1) {
dfs(i, -1);
}
}
return bcc.size();
}
Connectivity
Let
The graph below doesn’t have a cut-vertex but it has many vertex-cut sets,
- the set with minimum cardinality is called a minimum vertex-cut set
- a connected graph
contains a cut-vertex set only if is not complete
For a graph
- if
is a graph of order and size then
There are other measures of how connected a graph is, let
- for complete graph
of order ,