Articulation Points (or Cut Vertices) in a Graph (original) (raw)

Given an **undirected graph consisting of **V vertices and **E edges. The graph is represented as a 2D array edges[][], where each element edges[i] = [u, v] denotes an undirected edge between vertices u and v.
Find all the **articulation points in the graph. If no such points exist, return {-1}.

An articulation point is a vertex whose removal, along with all its connected edges, increases the number of connected components in the graph.

**Note: The graph may be disconnected, meaning it can consist of multiple connected components.

**Examples:

**Input: V = 5, edges[][] = [[0, 1], [1, 4], [4, 3], [4, 2], [2, 3]]

1

**Output: [1, 4]
**Explanation: Removing vertex 1 or 4 disconnects the graph, as illustrated below:

2

**Input: V = 4, edges[][] = [[0, 1], [0, 2]]
**Output: [0]
**Explanation: Removing the vertex 0 will increase the number of disconnected components to 3.

Table of Content

[Naive Approach] - Using DFS - O(V × (V + E)) Time and O(V) Space

The idea is to traverse all the vertices and check whether it could be an articulation point or not. For that, remove the chosen vertex and call DFS (Depth First Search) on its neighbors. If DFS needs to be called more than once (i.e., multiple disconnected components are formed), then removing that vertex increases the number of components, so it is an articulation point.

C++ `

//Driver Code Starts #include #include

using namespace std;

// Builds adjacency list from edge list vector<vector> constructadj(int V, vector<vector> &edges) {

vector<vector<int>> adj(V);

for (auto it : edges) {
    adj[it[0]].push_back(it[1]);
    adj[it[1]].push_back(it[0]);
}
return adj;

}

//Driver Code Ends

// Standard DFS to mark all reachable nodes void dfs(int node, vector<vector> &adj, vector &visited) { visited[node] = true;

for (int neighbor : adj[node]) {
    
    if (!visited[neighbor]) {
        dfs(neighbor, adj, visited);
    }
}

}

// Finds articulation points using naive DFS approach vector articulationPoints(int V, vector<vector>& adj ) {

vector<int> res;

// Try removing each node one by one
for (int i = 0; i < V; ++i) {
    vector<bool> visited(V, false);
    visited[i] = true;
    
    // count DFS calls from i's neighbors
    int comp = 0; 
    for (auto it : adj[i]) {
        
        // early stop if already more than 1 component
        if (comp > 1) break; 

        if (!visited[it]) {
            
            // explore connected part
            dfs(it, adj, visited); 
            comp++;
        }
    }

    // if more than one component forms, it's an articulation point
    if (comp > 1)
        res.push_back(i);
}

if (res.empty())
    return {-1};

return res;

}

//Driver Code Starts

int main() { int V = 5; vector<vector> edges = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}}; vector<vector> adj = constructadj(V, edges); vector ans = articulationPoints(V, adj);

for (auto it : ans) {
    cout << it << " ";
}
return 0;

}

//Driver Code Ends

Java

//Driver Code Starts import java.util.ArrayList; import java.util.Arrays;

class GfG {

// Builds adjacency list from edge list
static ArrayList<ArrayList<Integer>> constructadj(int V, int[][] edges) {
    ArrayList<ArrayList<Integer>> adj = new ArrayList<>();
    for (int i = 0; i < V; i++) {
        adj.add(new ArrayList<>());
    }

    for (int[] edge : edges) {
        adj.get(edge[0]).add(edge[1]);
        adj.get(edge[1]).add(edge[0]);
    }
    return adj;
}

//Driver Code Ends

// Standard DFS to mark all reachable nodes
static void dfs(int node,
ArrayList<ArrayList<Integer>> adj, boolean[] visited) {
    
    visited[node] = true;

    for (int neighbor : adj.get(node)) {
        if (!visited[neighbor]) {
            dfs(neighbor, adj, visited);
        }
    }
}

// Finds articulation points using naive DFS approach
static ArrayList<Integer> articulationPoints(int V, ArrayList<ArrayList<Integer>> adj) {
 
    ArrayList<Integer> res = new ArrayList<>();

    // Try removing each node one by one
    for (int i = 0; i < V; ++i) {
        boolean[] visited = new boolean[V];
        visited[i] = true;

        // count DFS calls from i's neighbors
        int comp = 0;
        for (int it : adj.get(i)) {

            // early stop if already more than 1 component
            if (comp > 1) break;

            if (!visited[it]) {

                // explore connected part
                dfs(it, adj, visited);
                comp++;
            }
        }

        // if more than one component forms, it's an articulation point
        if (comp > 1)
            res.add(i);
    }

    if (res.isEmpty())
        return new ArrayList<>(Arrays.asList(-1));

    return res;
}

//Driver Code Starts

public static void main(String[] args) {
    int V = 5;
    int[][] edges = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};
    
    ArrayList<ArrayList<Integer>> adj = constructadj(V, edges);
    ArrayList<Integer> ans = articulationPoints(V, adj);
    for (int it : ans) {
        System.out.print(it + " ");
    }
}

}

//Driver Code Ends

Python

#Driver Code Starts def constructadj(V, edges):

# Builds adjacency list from edge list
adj = [[] for _ in range(V)]
for u, v in edges:
    adj[u].append(v)
    adj[v].append(u)
return adj

#Driver Code Ends

Standard DFS to mark all reachable nodes

def dfs(node, adj, visited):

# Standard DFS to mark all reachable nodes
visited[node] = True

for neighbor in adj[node]:
    if not visited[neighbor]:
        dfs(neighbor, adj, visited)

def articulationPoints(V, adj): res = []

# Try removing each node one by one
for i in range(V):
    visited = [False] * V
    visited[i] = True 
    
    # count DFS calls from i's neighbors
    comp = 0  
    for it in adj[i]:
        if comp > 1:
            break 
        if not visited[it]:
            
            # explore connected part
            dfs(it, adj, visited)  
            comp += 1

    # if more than one component forms, it's an articulation point
    if comp > 1:
        res.append(i)

if not res:
    return [-1]

return res

#Driver Code Starts

if name == "main": V = 5 edges = [[0, 1], [1, 4], [2, 3], [2, 4], [3, 4]]

# Finds articulation points using naive DFS approach
adj = constructadj(V, edges)
ans = articulationPoints(V, adj)
for it in ans:
    print(it, end=" ")

#Driver Code Ends

C#

//Driver Code Starts using System; using System.Collections.Generic;

class GfG {

// Builds adjacency list from edge list
static List<List<int>> constructAdj(int V, int[,] edges) {
    
    List<List<int>> adj = new List<List<int>>();
    for (int i = 0; i < V; i++) {
        adj.Add(new List<int>());
    }

    int E = edges.GetLength(0);
    for (int i = 0; i < E; i++) {
        int u = edges[i, 0];
        int v = edges[i, 1];
        adj[u].Add(v);
        adj[v].Add(u);
    }

    return adj;
}

//Driver Code Ends

// Standard DFS to mark all reachable nodes
static void DFS(int node, List<List<int>> adj, bool[] visited) {
    visited[node] = true;

    foreach (int neighbor in adj[node]) {
        if (!visited[neighbor]) {
            DFS(neighbor, adj, visited);
        }
    }
}

// Finds articulation points using naive DFS approach
static List<int> articulationPoints(int V, List<List<int>>adj) {
    
    List<int> res = new List<int>();

    // Try removing each node one by one
    for (int i = 0; i < V; ++i) {
        bool[] visited = new bool[V];
        visited[i] = true;

        // count DFS calls from i's neighbors
        int comp = 0;
        foreach (int it in adj[i]) {
            
            // early stop if already more than 1 component
            if (comp > 1) break;

            if (!visited[it]) {
                // explore connected part
                DFS(it, adj, visited);
                comp++;
            }
        }

        // if more than one component forms, it's an articulation point
        if (comp > 1)
            res.Add(i);
    }

    if (res.Count == 0)
        return new List<int> { -1 };

    return res;
}

//Driver Code Starts

public static void Main(string[] args) {
    int V = 5;
    int[,] edges = new int[,] { { 0, 1 }, { 1, 4 }, { 2, 3 }, { 2, 4 }, { 3, 4 } };
    
    List<List<int>> adj = constructAdj(V, edges);
    List<int> ans = articulationPoints(V, adj);
    foreach (int it in ans) {
        Console.Write(it + " ");
    }
}

}

//Driver Code Ends

JavaScript

//Driver Code Starts // Builds adjacency list from edge list function constructadj(V, edges) { let adj = Array.from({ length: V }, () => []);

for (let [u, v] of edges) {
    adj[u].push(v);
    adj[v].push(u);
}

return adj;

}

//Driver Code Ends

// Standard DFS to mark all reachable nodes function dfs(node, adj, visited) { visited[node] = true;

for (let neighbor of adj[node]) {
    if (!visited[neighbor]) {
        dfs(neighbor, adj, visited);
    }
}

}

// Finds articulation points using naive DFS approach function articulationPoints(V, adj) {

const res = [];

// Try removing each node one by one
for (let i = 0; i < V; ++i) {
    let visited = Array(V).fill(false);
    visited[i] = true;

    // count DFS calls from i's neighbors
    let comp = 0;
    for (let it of adj[i]) {
        
        // early stop if already more than 1 component
        if (comp > 1) break;

        if (!visited[it]) {
            
            // explore connected part
            dfs(it, adj, visited);
            comp++;
        }
    }

    // if more than one component forms, it's an articulation point
    if (comp > 1)
        res.push(i);
}

if (res.length === 0)
    return [-1];

return res;

}

//Driver Code Starts

// Driver Code const V = 5; const edges = [[0, 1], [1, 4], [2, 3], [2, 4], [3, 4]];

const adj = constructadj(V, edges); const ans = articulationPoints(V, adj); console.log(ans.join(" "));

//Driver Code Ends

`

[Better Approach] - Using Tarjan's Algorithm - O(V + E) Time and O(V) Space

The idea is to use DFS to track how each node connects to its ancestors using discovery time and low values. Here, discovery time records when a node is first visited, and the low value represents the earliest (smallest) discovery time reachable from that node, including via back edges. For every node, we try to determine whether its subtree has an alternative path to reach an ancestor. If such a path does not exist for a child subtree, then the current node becomes a critical connection point, and removing it would disconnect that subtree from the rest of the graph. By propagating these low values during DFS, we can efficiently identify all nodes whose removal increases the number of connected components, i.e., the articulation points.

Let's understand with an example:

example-1drawio

For the vertex 3 (which is not the root), vertex 4 is the child of vertex 3. No vertex in the subtree rooted at vertex 4 has a back edge to one of ancestors of vertex 3. Thus on removal of vertex 3 and its associated edges the graph will get disconnected or the number of components in the graph will increase as the subtree rooted at vertex 4 will form a separate component. Hence vertex 3 is an articulation point.

Now consider the following graph:
Tree1drawio

Again the vertex 4 is the child of vertex 3. For the subtree rooted at vertex 4, vertex 7 in this subtree has a back edge to one of the ancestors of vertex 3 (which is vertex 1). Thus this subtree will not get disconnected on the removal of vertex 3 because of this back edge. Since there is no child v of vertex 3, such that subtree rooted at vertex v does not have a back edge to one of the ancestors of vertex 3. Hence vertex 3 is not an articulation point in this case.

**Step by Step implementation:

Maintain these arrays and integers and perform a DFS traversal.

Root Node Case:

For any non-root node u, check all its adjacent nodes:

Back Edge Case

After DFS traversal completes, all nodes marked as articulation points are stored in result array.

C++ `

//Driver Code Starts #include #include using namespace std;

vector<vector> constructAdj(int V, vector<vector> &edges) { vector<vector> adj(V);

for (auto &edge : edges) {
   
    adj[edge[0]].push_back(edge[1]);
    adj[edge[1]].push_back(edge[0]);
}
return adj;

}

//Driver Code Ends

// Helper function to perform DFS and find articulation points // using Tarjan's algorithm. void findPoints(vector<vector> &adj, int u, vector &visited, vector &disc, vector &low, int &time, int parent, vector &isAP) {

// Mark vertex u as visited and assign discovery
// time and low value
visited[u] = 1;
disc[u] = low[u] = ++time;
int children = 0; 

// Process all adjacent vertices of u
for (int v : adj[u]) {
    
    // If v is not visited, then recursively visit it
    if (!visited[v]) {
        children++;
        findPoints(adj, v, visited, disc, low, time, u, isAP);

        // Check if the subtree rooted at v has a 
        // connection to one of the ancestors of u
        low[u] = min(low[u], low[v]);

        // If u is not a root and low[v] is greater than or equal to disc[u],
        // then u is an articulation point
        if (parent != -1 && low[v] >= disc[u]) {
            isAP[u] = 1;
        }
    } 
    
    // Update low value of u for back edge
    else if (v != parent) {
        low[u] = min(low[u], disc[v]);
    }
}

// If u is root of DFS tree and has more than 
// one child, it is an articulation point
if (parent == -1 && children > 1) {
    isAP[u] = 1;
}

}

// Main function to find articulation points in the graph vector articulationPoints(int V, vector<vector>& adj) {

vector<int> disc(V, 0), low(V, 0), visited(V, 0), isAP(V, 0);
int time = 0; 

// Run DFS from each vertex if not
// already visited (to handle disconnected graphs)
for (int u = 0; u < V; u++) {
    if (!visited[u]) {
        findPoints(adj, u, visited, disc, low, time, -1, isAP);
    }
}

// Collect all vertices that are articulation points
vector<int> result;
for (int u = 0; u < V; u++) {
    if (isAP[u]) {
        result.push_back(u);
    }
}

// If no articulation points are found, return vector containing -1
return result.empty() ? vector<int>{-1} : result;

}

//Driver Code Starts

int main() { int V = 5; vector<vector> edges = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};

vector<vector<int>> adj = constructAdj(V, edges);
vector<int> ans = articulationPoints(V,adj);

for (int u : ans) {
    cout << u << " ";
}
cout << endl;

return 0;

}

//Driver Code Ends

Java

//Driver Code Starts import java.util.ArrayList;

class GfG {

 static ArrayList<ArrayList<Integer>> constructAdj(int V, int[][] edges) {
    ArrayList<ArrayList<Integer>> adj = new ArrayList<>();
    for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
    
    for (int[] edge : edges) {
        adj.get(edge[0]).add(edge[1]);
        adj.get(edge[1]).add(edge[0]);
    }
    return adj;
}

//Driver Code Ends

// Helper function to perform DFS and find articulation points
// using Tarjan's algorithm.
 static void findPoints(ArrayList<ArrayList<Integer>> adj, int u, int[] visited,
                              int[] disc, int[] low, 
                              int[] time, int parent, int[] isAP) {
                                  
    // Mark vertex u as visited and assign discovery
    // time and low value
    visited[u] = 1;
    disc[u] = low[u] = ++time[0];
    int children = 0; 

    // Process all adjacent vertices of u
    for (int v : adj.get(u)) {
        
        // If v is not visited, then recursively visit it
        if (visited[v] == 0) {
            children++;
            findPoints(adj, v, visited, disc, low, time, u, isAP);

            // Check if the subtree rooted at v has a 
            // connection to one of the ancestors of u
            low[u] = Math.min(low[u], low[v]);

            // If u is not a root and low[v] is greater 
            // than or equal to disc[u],
            // then u is an articulation point
            if (parent != -1 && low[v] >= disc[u]) {
                isAP[u] = 1;
            }
        } 
        
        // Update low value of u for back edge
        else if (v != parent) {
            low[u] = Math.min(low[u], disc[v]);
        }
    }

    // If u is root of DFS tree and has more than 
    // one child, it is an articulation point
    if (parent == -1 && children > 1) {
        isAP[u] = 1;
    }
}

// Main function to find articulation points in the graph
 static ArrayList<Integer> articulationPoints(int V, ArrayList<ArrayList<Integer>> adj) {
    
    int[] disc = new int[V], low = new int[V],
    visited = new int[V], isAP = new int[V];
    int[] time = {0}; 

    // Run DFS from each vertex if not
    // already visited (to handle disconnected graphs)
    for (int u = 0; u < V; u++) {
        if (visited[u] == 0) {
            findPoints(adj, u, visited, disc, low, time, -1, isAP);
        }
    }

    // Collect all vertices that are articulation points
    ArrayList<Integer> result = new ArrayList<>();
    for (int u = 0; u < V; u++) {
        if (isAP[u] == 1) {
            result.add(u);
        }
    }

    // If no articulation points are found, return list containing -1
    if (result.isEmpty()) result.add(-1);
    return result;
}

//Driver Code Starts

public static void main(String[] args) {
    int V = 5; 
    int[][] edges = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};
    
    ArrayList<ArrayList<Integer>> adj = constructAdj(V, edges);
    ArrayList<Integer> ans = articulationPoints(V, adj);

    for (int u : ans) {
        System.out.print(u + " ");
    }
    System.out.println();
}

}

//Driver Code Ends

Python

#Driver Code Starts def constructAdj(V, edges): adj = [[] for _ in range(V)]

for edge in edges:
    adj[edge[0]].append(edge[1])
    adj[edge[1]].append(edge[0])
return adj

#Driver Code Ends

Helper function to perform DFS and find articulation points

using Tarjan's algorithm.

def findPoints(adj, u, visited, disc, low, time, parent, isAP):

# Mark vertex u as visited and assign discovery
# time and low value
visited[u] = 1
time[0] += 1
disc[u] = low[u] = time[0]
children = 0

# Process all adjacent vertices of u
for v in adj[u]:
    
    # If v is not visited, then recursively visit it
    if not visited[v]:
        children += 1
        findPoints(adj, v, visited, disc, low, time, u, isAP)

        # Check if the subtree rooted at v has a 
        # connection to one of the ancestors of u
        low[u] = min(low[u], low[v])

        # If u is not a root and low[v] is greater than or equal to disc[u],
        # then u is an articulation point
        if parent != -1 and low[v] >= disc[u]:
            isAP[u] = 1

    # Update low value of u for back edge
    elif v != parent:
        low[u] = min(low[u], disc[v])

# If u is root of DFS tree and has more than 
# one child, it is an articulation point
if parent == -1 and children > 1:
    isAP[u] = 1

Main function to find articulation points in the graph

def articulationPoints(V, adj):

disc = [0] * V
low = [0] * V
visited = [0] * V
isAP = [0] * V
time = [0]  

# Run DFS from each vertex if not
# already visited (to handle disconnected graphs)
for u in range(V):
    if not visited[u]:
        findPoints(adj, u, visited, disc, low, time, -1, isAP)

# Collect all vertices that are articulation points
result = [u for u in range(V) if isAP[u]]

# If no articulation points are found, return list containing -1
return result if result else [-1]

#Driver Code Starts

if name == "main": V = 5 edges = [[0, 1], [1, 4], [2, 3], [2, 4], [3, 4]]

adj = constructAdj(V, edges)
ans = articulationPoints(V, adj)

for u in ans:
    print(u, end=' ')
print()

#Driver Code Ends

C#

//Driver Code Starts using System; using System.Collections.Generic;

class GfG { static List<List> constructAdj(int V, int[,] edges) { List<List> adj = new List<List>(); for (int i = 0; i < V; i++) { adj.Add(new List()); }

    int M = edges.GetLength(0);
    for (int i = 0; i < M; i++) {
        int u = edges[i, 0];
        int v = edges[i, 1];
        adj[u].Add(v);
        adj[v].Add(u);
    }

    return adj;
}

//Driver Code Ends

// Helper function to perform DFS and find articulation points
// using Tarjan's algorithm.
static void findPoints(List<List<int>> adj, int u, List<int> visited,
                       List<int> disc, List<int> low,
                       ref int time, int parent, List<int> isAP) {
                           
    // Mark vertex u as visited and assign discovery
    // time and low value
    visited[u] = 1;
    disc[u] = low[u] = ++time;
    int children = 0;

    // Process all adjacent vertices of u
    foreach (int v in adj[u])
    {
        // If v is not visited, then recursively visit it
        if (visited[v] == 0)
        {
            children++;
            findPoints(adj, v, visited, disc, low, ref time, u, isAP);

            // Check if the subtree rooted at v has a 
            // connection to one of the ancestors of u
            low[u] = Math.Min(low[u], low[v]);

            // If u is not a root and low[v] is greater 
            // than or equal to disc[u],
            // then u is an articulation point
            if (parent != -1 && low[v] >= disc[u])
            {
                isAP[u] = 1;
            }
        }

        // Update low value of u for back edge
        else if (v != parent)
        {
            low[u] = Math.Min(low[u], disc[v]);
        }
    }

    // If u is root of DFS tree and has more than 
    // one child, it is an articulation point
    if (parent == -1 && children > 1)
    {
        isAP[u] = 1;
    }
}

// Main function to find articulation points in the graph
static List<int> articulationPoints(int V, List<List<int>> adj)
{

    List<int> disc = new List<int>(new int[V]);
    List<int> low = new List<int>(new int[V]);
    List<int> visited = new List<int>(new int[V]);
    List<int> isAP = new List<int>(new int[V]);
    int time = 0;

    // Run DFS from each vertex if not
    // already visited (to handle disconnected graphs)
    for (int u = 0; u < V; u++)
    {
        if (visited[u] == 0)
        {
            findPoints(adj, u, visited, disc, low, ref time, -1, isAP);
        }
    }

    // Collect all vertices that are articulation points
    List<int> result = new List<int>();
    for (int u = 0; u < V; u++)
    {
        if (isAP[u] == 1)
        {
            result.Add(u);
        }
    }

    // If no articulation points are found, return list containing -1
    return result.Count == 0 ? new List<int> { -1 } : result;
}

//Driver Code Starts

static void Main()
{
    int V = 5;
    int[,] edges = {
        {0, 1},
        {1, 4},
        {2, 3},
        {2, 4},
        {3, 4}
    };

    List<List<int>> adj = constructAdj(V, edges);
    List<int> ans = articulationPoints(V, adj);

    foreach (int u in ans)
    {
        Console.Write(u + " ");
    }
    Console.WriteLine();
}

}

//Driver Code Ends

JavaScript

//Driver Code Starts // Build adjacency list from edge list function constructAdj(V, edges) { const adj = Array.from({ length: V }, () => []);

for (let i = 0; i < edges.length; i++) {
    const [u, v] = edges[i];
    adj[u].push(v);
    adj[v].push(u);
}

return adj;

}

//Driver Code Ends

// Helper function to perform DFS and find articulation points // using Tarjan's algorithm. function findPoints(adj, u, visited, disc, low, timeRef, parent, isAP) {

// Mark vertex u as visited and assign discovery
// time and low value
visited[u] = 1;
disc[u] = low[u] = ++timeRef.value;
let children = 0;

// Process all adjacent vertices of u
for (let v of adj[u]) {
    
    // If v is not visited, then recursively visit it
    if (!visited[v]) {
        children++;
        findPoints(adj, v, visited, disc, low, timeRef, u, isAP);

        // Check if the subtree rooted at v has a 
        // connection to one of the ancestors of u
        low[u] = Math.min(low[u], low[v]);

        // If u is not a root and low[v] is greater 
        // than or equal to disc[u],
        // then u is an articulation point
        if (parent !== -1 && low[v] >= disc[u]) {
            isAP[u] = 1;
        }
    }
    
    // Update low value of u for back edge
    else if (v !== parent) {
        low[u] = Math.min(low[u], disc[v]);
    }
}

// If u is root of DFS tree and has more than 
// one child, it is an articulation point
if (parent === -1 && children > 1) {
    isAP[u] = 1;
}

}

// Main function to find articulation points in the graph function articulationPoints(V, adj) {

const disc = Array(V).fill(0);
const low = Array(V).fill(0);
const visited = Array(V).fill(0);
const isAP = Array(V).fill(0);
const timeRef = { value: 0 };

// Run DFS from each vertex if not
// already visited (to handle disconnected graphs)
for (let u = 0; u < V; u++) {
    if (!visited[u]) {
        findPoints(adj, u, visited, disc, low, timeRef, -1, isAP);
    }
}

// Collect all vertices that are articulation points
const result = [];
for (let u = 0; u < V; u++) {
    if (isAP[u]) {
        result.push(u);
    }
}

// If no articulation points are found, return array containing -1
return result.length === 0 ? [-1] : result;

}

//Driver Code Starts

// Driver Code const V = 5; const edges = [ [0, 1], [1, 4], [2, 3], [2, 4], [3, 4] ];

const adj = constructAdj(V, edges); const ans = articulationPoints(V, adj); console.log(ans.join(' '));

//Driver Code Ends

`

[Expected Approach] - Using Tarjan’s Algorithm (Iterative Method) - O(V + E) Time and O(V) Space

We can use Tarjan’s algorithm with an explicit stack to manage the traversal state instead of relying on the system’s call stack. The logic remains the same that we maintain disc, low, parent, and childrenCount but we manually control how nodes are processed.

For each node, we store its current state, including which neighbor to visit next and whether the node is in the entering or exiting phase.

With this approach, we preserve the core idea of Tarjan’s algorithm while making it more suitable for large or deep graphs.

C++ `

//Driver Code Starts #include #include #include using namespace std;

//Driver Code Ends

void tarjan(int start, vector<vector>& adj, vector& visited, vector& disc, vector& low, vector& parent, vector& childrenCount, vector& ap) {

// Stack stores {node, neighborIndex, state}
int n = adj.size();
vector<array<int, 3>> stack(n * 2);
int top = 0, time = 0;

stack[top++] = {start, 0, 0};

while (top > 0) {
    int u = stack[top - 1][0], idx = stack[top - 1][1], state = stack[top - 1][2];
    top--;

    if (state == 0) {

        // First visit: mark discovery and low
        if (!visited[u]) {
            visited[u] = true;
            disc[u] = low[u] = ++time;
        }

        // Process neighbors
        if (idx < (int)adj[u].size()) {
            int v = adj[u][idx];

            // Push current node with next neighbor
            stack[top++] = {u, idx + 1, 0};

            // Visit child
            if (!visited[v]) {
                parent[v] = u;
                childrenCount[u]++;
                stack[top++] = {v, 0, 0};
            } 
            // Back edge
            else if (v != parent[u]) {
                low[u] = min(low[u], disc[v]);
            }
        } 
        // Finished neighbors, push exit state
        else {
            stack[top++] = {u, 0, 1};
        }
    } 
    else {

        // Update parent low and mark articulation
        if (top > 0) {
            int p = stack[top - 1][0];
            low[p] = min(low[p], low[u]);
            if (parent[p] != -1 && low[u] >= disc[p]) ap[p] = true;
        } 
        // Root articulation check
        else if (childrenCount[start] > 1) {
            ap[start] = true;
        }
    }
}

}

// Main function to find articulation points vector articulationPoints(int V, vector<vector>& adj) {

// Initialize arrays
vector<int> disc(V, 0), low(V, 0), parent(V, -1), childrenCount(V, 0);
vector<bool> visited(V, false), ap(V, false);

// Run iterative DFS for each component
for (int i = 0; i < V; i++) {
    if (!visited[i]) tarjan(i, adj, visited, disc, low, parent, childrenCount, ap);
}

// Collect articulation points
vector<int> result;
for (int i = 0; i < V; i++) if (ap[i]) result.push_back(i);
if (result.empty()) result.push_back(-1);
return result;

}

//Driver Code Starts int main() { int V = 5; int edges[5][2] = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};

// Build adjacency list
vector<vector<int>> adj(V);
for (auto &e : edges) {
    adj[e[0]].push_back(e[1]);
    adj[e[1]].push_back(e[0]);
}

vector<int> ans = articulationPoints(V, adj);
for (int x : ans) cout << x << " ";
cout << "

"; } //Driver Code Ends

Java

//Driver Code Starts import java.util.ArrayList;

class GfG {

//Driver Code Ends

static ArrayList<Integer> articulationPoints(int V, ArrayList<ArrayList<Integer>> adj) {
    
    int[] disc = new int[V], low = new int[V], parent = new int[V], childrenCount = new int[V];
    boolean[] visited = new boolean[V], ap = new boolean[V];
    for (int i = 0; i < V; i++) parent[i] = -1;

    // Run iterative DFS for each component
    for (int i = 0; i < V; i++) {
        if (!visited[i]) tarjan(i, adj, visited, disc, low, parent, childrenCount, ap);
    }

    // Collect articulation points
    ArrayList<Integer> result = new ArrayList<>();
    for (int i = 0; i < V; i++) if (ap[i]) result.add(i);
    if (result.isEmpty()) result.add(-1);
    return result;
}

private static void tarjan(int start, ArrayList<ArrayList<Integer>> adj, boolean[] visited,
                                 int[] disc, int[] low, int[] parent, int[] childrenCount, boolean[] ap) {

    // Stack stores {node, neighborIndex, state}
    int[][] stack = new int[visited.length * 2][3];
    int top = 0, time = 0;

    stack[top++] = new int[]{start, 0, 0};

    while (top > 0) {
        int u = stack[top - 1][0], idx = stack[top - 1][1], state = stack[top - 1][2];
        top--;

        if (state == 0) {

            // First visit: mark discovery and low
            if (!visited[u]) {
                visited[u] = true;
                disc[u] = low[u] = ++time;
            }

            // Process neighbors
            if (idx < adj.get(u).size()) {
                int v = adj.get(u).get(idx);

                // Push current node with next neighbor
                stack[top++] = new int[]{u, idx + 1, 0};

                // Visit child
                if (!visited[v]) {
                    parent[v] = u;
                    childrenCount[u]++;
                    stack[top++] = new int[]{v, 0, 0};
                } 
                
                // Back edge
                else if (v != parent[u]) {
                    low[u] = Math.min(low[u], disc[v]);
                }
            } 
            
            // Finished neighbors, push exit state
            else {
                stack[top++] = new int[]{u, 0, 1};
            }
        } 

        // Exiting node
        else {

            // Update parent low and mark articulation
            if (top > 0) {
                int p = stack[top - 1][0];
                low[p] = Math.min(low[p], low[u]);
                if (parent[p] != -1 && low[u] >= disc[p]) ap[p] = true;
            } 
            
            // Root articulation check
            else if (childrenCount[start] > 1) {
                ap[start] = true;
            }
        }
    }
}

//Driver Code Starts

public static void main(String[] args) {
    int V = 5;

    // Build adjacency list
    ArrayList<ArrayList<Integer>> adj = new ArrayList<>();
    for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
    int[][] edges = {{0, 1}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};
    for (int[] e : edges) {
        adj.get(e[0]).add(e[1]);
        adj.get(e[1]).add(e[0]);
    }

    ArrayList<Integer> ans = articulationPoints(V, adj);

    for (int x : ans) {
        System.out.print(x + " ");
    }
}

} //Driver Code Ends

Python

def articulation_points(V, adj):

# discovery times
disc = [0] * V  

# low values
low = [0] * V   
parent = [-1] * V
children_count = [0] * V
visited = [False] * V
ap = [False] * V

# Run iterative DFS for each component
for i in range(V):
    if not visited[i]:
        tarjan(i, adj, visited, disc, low, parent, children_count, ap)

# Collect articulation points
result = [i for i in range(V) if ap[i]]
return result if result else [-1]

def tarjan(start, adj, visited, disc, low, parent, children_count, ap):

# Stack stores {node, neighborIndex, state}
# state 0 = entering, 1 = exiting
stack = [[start, 0, 0]]  
time = 0

while stack:
    u, idx, state = stack.pop()

    if state == 0:
        
        # First visit: mark discovery and low
        if not visited[u]:
            visited[u] = True
            time += 1
            disc[u] = low[u] = time

        # Process neighbors
        if idx < len(adj[u]):
            v = adj[u][idx]

            # Push current node with next neighbor
            stack.append([u, idx + 1, 0])

            # Visit child
            if not visited[v]:
                parent[v] = u
                children_count[u] += 1
                stack.append([v, 0, 0])
            
            # Back edge
            elif v != parent[u]:
                low[u] = min(low[u], disc[v])
        else:
            # Finished neighbors, push exit state
            stack.append([u, 0, 1])

    else:
        
        # Update parent low and mark articulation
        if stack:
            p = stack[-1][0]
            low[p] = min(low[p], low[u])
            if parent[p] != -1 and low[u] >= disc[p]:
                ap[p] = True
                
        # Root articulation check
        else:
            if children_count[start] > 1:
                ap[start] = True

#Driver Code Starts if name == "main": V = 5

# Build adjacency list
adj = [[] for _ in range(V)]
edges = [(0, 1), (1, 4), (2, 3), (2, 4), (3, 4)]
for u, v in edges:
    adj[u].append(v)
    adj[v].append(u)

print(*articulation_points(V, adj))

#Driver Code Ends

C#

//Driver Code Starts using System; using System.Collections.Generic;

class Solution { //Driver Code Ends

private void tarjan(int start, List<List<int>> adj, bool[] visited,
                          int[] disc, int[] low, int[] parent, int[] childrenCount, bool[] ap)
{
    // Stack stores {node, neighborIndex, state}
    int n = adj.Count;
    var stack = new List<int[]>(n * 2);
    int time = 0;

    stack.Add(new int[] { start, 0, 0 });

    while (stack.Count > 0)
    {
        var topElem = stack[stack.Count - 1];
        stack.RemoveAt(stack.Count - 1);
        int u = topElem[0], idx = topElem[1], state = topElem[2];

        if (state == 0)
        {
            // First visit: mark discovery and low
            if (!visited[u])
            {
                visited[u] = true;
                disc[u] = low[u] = ++time;
            }

            // Process neighbors
            if (idx < adj[u].Count)
            {
                int v = adj[u][idx];

                // Push current node with next neighbor
                stack.Add(new int[] { u, idx + 1, 0 });

                // Visit child
                if (!visited[v])
                {
                    parent[v] = u;
                    childrenCount[u]++;
                    stack.Add(new int[] { v, 0, 0 });
                }
                // Back edge
                else if (v != parent[u])
                {
                    low[u] = Math.Min(low[u], disc[v]);
                }
            }
            // Finished neighbors, push exit state
            else
            {
                stack.Add(new int[] { u, 0, 1 });
            }
        }
        else
        {
            // Update parent low and mark articulation
            if (stack.Count > 0)
            {
                int p = stack[stack.Count - 1][0];
                low[p] = Math.Min(low[p], low[u]);
                if (parent[p] != -1 && low[u] >= disc[p]) ap[p] = true;
            }
            // Root articulation check
            else if (childrenCount[start] > 1)
            {
                ap[start] = true;
            }
        }
    }
}

// Main function to find articulation points
public List<int> articulationPoints(int V, List<List<int>> adj)
{
    // Initialize arrays
    int[] disc = new int[V], low = new int[V], parent = new int[V], childrenCount = new int[V];
    bool[] visited = new bool[V], ap = new bool[V];
    for (int i = 0; i < V; i++) parent[i] = -1;

    // Run iterative DFS for each component
    for (int i = 0; i < V; i++)
    {
        if (!visited[i])
        {
            tarjan(i, adj, visited, disc, low, parent, childrenCount, ap);
        }
    }

    // Collect articulation points
    var result = new List<int>();
    for (int i = 0; i < V; i++) if (ap[i]) result.Add(i);
    if (result.Count == 0) result.Add(-1);
    return result;
}

//Driver Code Starts static void Main() { int V = 5;

    // Build adjacency list
    var adj = new List<List<int>>();
    for (int i = 0; i < V; i++) adj.Add(new List<int>());
    int[][] edges = new int[][]
    {
        new int[]{0,1}, new int[]{1,4}, new int[]{2,3}, new int[]{2,4}, new int[]{3,4}
    };
    foreach (var e in edges)
    {
        adj[e[0]].Add(e[1]);
        adj[e[1]].Add(e[0]);
    }

    var sol = new Solution();
    var ans = sol.articulationPoints(V, adj);
    Console.WriteLine(string.Join(" ", ans));
}

} //Driver Code Ends

JavaScript

function articulationPoints(V, adj) { // Initialize arrays const disc = Array(V).fill(0); const low = Array(V).fill(0); const parent = Array(V).fill(-1); const childrenCount = Array(V).fill(0); const visited = Array(V).fill(false); const ap = Array(V).fill(false);

// Run iterative DFS for each component
for (let i = 0; i < V; i++) {
    if (!visited[i]) {
        tarjan(i, adj, visited, disc, low, parent, childrenCount, ap);
    }
}

// Collect articulation points
const result = [];
for (let i = 0; i < V; i++) if (ap[i]) result.push(i);
if (result.length === 0) result.push(-1);
return result;

}

function tarjan(start, adj, visited, disc, low, parent, childrenCount, ap) { // Stack stores {node, neighborIndex, state} const stack = []; let time = 0;

stack.push([start, 0, 0]); // state 0 = entering, 1 = exiting

while (stack.length > 0) {
    const [u, idx, state] = stack.pop();

    if (state === 0) {

        // First visit: mark discovery and low
        if (!visited[u]) {
            visited[u] = true;
            disc[u] = low[u] = ++time;
        }

        // Process neighbors
        if (idx < adj[u].length) {
            const v = adj[u][idx];

            // Push current node with next neighbor
            stack.push([u, idx + 1, 0]);

            // Visit child
            if (!visited[v]) {
                parent[v] = u;
                childrenCount[u]++;
                stack.push([v, 0, 0]);
            } 
            // Back edge
            else if (v !== parent[u]) {
                low[u] = Math.min(low[u], disc[v]);
            }
        } 
        // Finished neighbors, push exit state
        else {
            stack.push([u, 0, 1]);
        }
    } 
    // Exiting node
    else {

        // Update parent low and mark articulation
        if (stack.length > 0) {
            const p = stack[stack.length - 1][0];
            low[p] = Math.min(low[p], low[u]);
            if (parent[p] !== -1 && low[u] >= disc[p]) ap[p] = true;
        } 
        // Root articulation check
        else if (childrenCount[start] > 1) {
            ap[start] = true;
        }
    }
}

}

//Driver Code Starts // Driver code const V = 5;

// Build adjacency list const adj = Array.from({ length: V }, () => []); const edges = [[0, 1], [1, 4], [2, 3], [2, 4], [3, 4]]; for (const [u, v] of edges) { adj[u].push(v); adj[v].push(u); }

console.log(articulationPoints(V, adj).join(" ")); //Driver Code Ends

`