Articulation Points in Graph in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find articulation points changes as the graph grows.
How does the algorithm's work increase when the graph has more nodes and edges?
Analyze the time complexity of the following code snippet.
fn dfs(
u: usize,
parent: Option<usize>,
time: &mut usize,
graph: &Vec<Vec<usize>>,
visited: &mut Vec<bool>,
disc: &mut Vec<usize>,
low: &mut Vec<usize>,
articulation: &mut Vec<bool>
) {
visited[u] = true;
*time += 1;
disc[u] = *time;
low[u] = *time;
let mut children = 0;
for &v in &graph[u] {
if !visited[v] {
children += 1;
dfs(v, Some(u), time, graph, visited, disc, low, articulation);
low[u] = std::cmp::min(low[u], low[v]);
if parent.is_none() && children > 1 {
articulation[u] = true;
}
if parent.is_some() && low[v] >= disc[u] {
articulation[u] = true;
}
} else if Some(v) != parent {
low[u] = std::cmp::min(low[u], disc[v]);
}
}
}
This code finds articulation points using a depth-first search on the graph.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Depth-first search (DFS) visits each node and its edges.
- How many times: Each node and edge is visited once during DFS.
As the graph grows, the algorithm visits every node and edge once.
| Input Size (n nodes, m edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 visits (nodes + edges) |
| 100 nodes, 200 edges | About 300 visits |
| 1000 nodes, 5000 edges | About 6000 visits |
Pattern observation: The work grows roughly in proportion to the total nodes plus edges.
Time Complexity: O(n + m)
This means the time grows linearly with the number of nodes and edges in the graph.
[X] Wrong: "The algorithm checks every pair of nodes, so it must be O(n²)."
[OK] Correct: The DFS only visits each node and edge once, not every pair, so it is faster than O(n²).
Understanding this time complexity helps you explain how graph algorithms scale and why DFS-based methods are efficient for connectivity problems.
"What if the graph was represented as an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand articulation point definition
An articulation point is a vertex that, if removed, breaks the graph into more pieces (connected components).Step 2: Compare options with definition
Only A vertex that, when removed, increases the number of connected components matches this definition exactly.Final Answer:
A vertex that, when removed, increases the number of connected components -> Option BQuick Check:
Articulation point = vertex increasing components when removed [OK]
- Confusing articulation point with highest degree vertex
- Thinking articulation point is a vertex with no edges
- Mixing articulation point with cycle forming vertex
Solution
Step 1: Identify graph representation for articulation points
Graphs are commonly stored as adjacency lists for efficient traversal and edge lookup.Step 2: Match Rust data structures
Vec represents adjacency lists well, storing neighbors for each vertex.Final Answer:
Vec (adjacency list) -> Option AQuick Check:
Adjacency list = Vec in Rust [OK]
- Using HashMap which is unrelated
- Using simple Vec<usize> which can't store edges
- Using String which is not a graph structure
let graph = vec![
vec![1, 2],
vec![0, 2],
vec![0, 1, 3],
vec![2, 4],
vec![3]
];
// Articulation points found: [2, 3]
println!("{:?}", find_articulation_points(&graph));Solution
Step 1: Analyze graph structure
Vertices 2 and 3 connect parts of the graph such that removing them increases components.Step 2: Identify articulation points
Removing vertex 2 disconnects vertex 3 and 4; removing vertex 3 disconnects vertex 4.Final Answer:
[2, 3] -> Option CQuick Check:
Vertices 2 and 3 are articulation points [OK]
- Choosing vertices with no articulation role
- Ignoring connectivity after removal
- Assuming leaf nodes are articulation points
fn dfs(u: usize, graph: &Vec, visited: &mut Vec<bool>, disc: &mut Vec<usize>, low: &mut Vec<usize>, parent: &mut Vec, time: &mut usize) {
visited[u] = true;
disc[u] = *time;
low[u] = *time;
*time += 1;
for &v in &graph[u] {
if !visited[v] {
parent[v] = Some(u);
dfs(v, graph, visited, disc, low, parent, time);
low[u] = low[u].min(low[v]);
} else if v != parent[u].unwrap() {
low[u] = low[u].min(disc[v]);
}
}
}Solution
Step 1: Check unwrap usage on parent[u]
parent[u] may be None for root node, unwrap() causes panic if None.Step 2: Identify safe handling
Code should check if parent[u] is Some before unwrap or handle root separately.Final Answer:
Using unwrap() on parent[u] without checking if it is Some -> Option DQuick Check:
Unsafe unwrap on Option causes runtime panic [OK]
- Ignoring root node parent None case
- Assuming unwrap() is always safe
- Missing visited marking (actually present here)
Solution
Step 1: Understand disconnected graph scenario
Articulation points can exist in any component, so all must be checked.Step 2: Adapt algorithm to run DFS on all components
Run DFS from every unvisited vertex to cover all disconnected parts.Final Answer:
Run DFS from every unvisited vertex to cover all components -> Option AQuick Check:
Multiple DFS runs cover all components [OK]
- Assuming graph is always connected
- Using BFS which doesn't track articulation points easily
- Ignoring disconnected components
Ready to prove it?
Pass the test and this pattern moves unknown
