class Graph { adjacencyList: Map<number, number[]> = new Map(); time = 0; addEdge(u: number, v: number) { if (!this.adjacencyList.has(u)) this.adjacencyList.set(u, []); if (!this.adjacencyList.has(v)) this.adjacencyList.set(v, []); this.adjacencyList.get(u)!.push(v); this.adjacencyList.get(v)!.push(u); } findBridges(): [number, number][] { const visited = new Set<number>(); const disc = new Map<number, number>(); const low = new Map<number, number>(); const parent = new Map<number, number | null>(); const bridges: [number, number][] = []; const dfs = (u: number) => { visited.add(u); this.time++; disc.set(u, this.time); low.set(u, this.time); for (const v of this.adjacencyList.get(u) || []) { if (!visited.has(v)) { parent.set(v, u); dfs(v); low.set(u, Math.min(low.get(u)!, low.get(v)!)); if (low.get(v)! > disc.get(u)!) { bridges.push([u, v]); } } else if (v !== parent.get(u)) { low.set(u, Math.min(low.get(u)!, disc.get(v)!)); } } }; for (const node of this.adjacencyList.keys()) { if (!visited.has(node)) { parent.set(node, null); dfs(node); } } return bridges; } } const g = new Graph(); g.addEdge(1, 2); g.addEdge(1, 3); g.addEdge(3, 4); g.addEdge(3, 5); g.addEdge(4, 5); console.log(g.findBridges());
The graph has edges: 1-2, 1-3, 3-4, 3-5, 4-5.
Edges 3-4 and 3-5 form a cycle with 4-5, so removing them does not disconnect the graph.
Edge 1-3 connects node 1 to the cycle, so removing it disconnects node 1 from the rest.
Edge 1-2 is a leaf edge, removing it disconnects node 2.
Thus, bridges are edges [1, 2] and [1, 3].
The 'low' value of a node is the smallest discovery time reachable from that node by following zero or more tree edges and then possibly one back edge.
This helps detect if there is a back edge connecting a descendant to an ancestor, which means no bridge on that path.
class Graph { adjacencyList: Map<number, number[]> = new Map(); time = 0; addEdge(u: number, v: number) { if (!this.adjacencyList.has(u)) this.adjacencyList.set(u, []); if (!this.adjacencyList.has(v)) this.adjacencyList.set(v, []); this.adjacencyList.get(u)!.push(v); this.adjacencyList.get(v)!.push(u); } findBridges(): [number, number][] { const visited = new Set<number>(); const disc = new Map<number, number>(); const low = new Map<number, number>(); const parent = new Map<number, number | null>(); const bridges: [number, number][] = []; const dfs = (u: number) => { visited.add(u); this.time++; disc.set(u, this.time); low.set(u, this.time); for (const v of this.adjacencyList.get(u) || []) { if (!visited.has(v)) { parent.set(v, u); dfs(v); low.set(u, Math.min(low.get(u)!, low.get(v)!)); if (low.get(v)! >= disc.get(u)!) { bridges.push([u, v]); } } else if (v !== parent.get(u)) { low.set(u, Math.min(low.get(u)!, disc.get(v)!)); } } }; for (const node of this.adjacencyList.keys()) { if (!visited.has(node)) { parent.set(node, null); dfs(node); } } return bridges; } } const g = new Graph(); g.addEdge(1, 2); g.addEdge(1, 3); g.addEdge(3, 4); g.addEdge(3, 5); g.addEdge(4, 5); console.log(g.findBridges());
The condition if (low.get(v)! >= disc.get(u)!) is incorrect for bridges.
It should be if (low.get(v)! > disc.get(u)!) because equality means the edge is part of a cycle, not a bridge.
Using >= causes edges that are not bridges to be included.
A complete graph has every possible edge between nodes.
Removing any edge does not disconnect the graph because there are many alternative paths.
Therefore, there are no bridges in a complete graph.
Tarjan's algorithm relies on discovery times from DFS to detect back edges and cycles.
BFS does not produce a DFS tree with discovery times, so it cannot identify bridges using this method.