Skip to content

Commit 25703c3

Browse files
committedMay 11, 2018
Add Tarjan's algorithm.
1 parent 21d4144 commit 25703c3

File tree

5 files changed

+327
-2
lines changed

5 files changed

+327
-2
lines changed
 

‎README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
7373
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
7474
* [Topological Sorting](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/topological-sorting) - DFS method
75-
* [Articulation Points](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/articulation-points) - Tarjan's algorithm
75+
* [Articulation Points](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/articulation-points) - Tarjan's algorithm (DFS based)
76+
* [Bridges](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bridges) - DFS based algorithm
7677
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path)
7778
* Strongly Connected Component algorithm
7879
* Shortest Path Faster Algorithm (SPFA)

‎src/algorithms/graph/articulation-points/articulationPoints.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class VisitMetadata {
1414
}
1515

1616
/**
17-
* Tarjan's algorithm for rinding articulation points in graph.
17+
* Tarjan's algorithm for finding articulation points in graph.
1818
*
1919
* @param {Graph} graph
2020
* @return {GraphVertex[]}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Bridges in Graph
2+
3+
In graph theory, a **bridge**, **isthmus**, **cut-edge**, or **cut arc** is an edge
4+
of a graph whose deletion increases its number of connected components. Equivalently,
5+
an edge is a bridge if and only if it is not contained in any cycle. A graph is said
6+
to be bridgeless or isthmus-free if it contains no bridges.
7+
8+
![Bridges in graph](https://upload.wikimedia.org/wikipedia/commons/d/df/Graph_cut_edges.svg)
9+
10+
A graph with 16 vertices and 6 bridges (highlighted in red)
11+
12+
![Bridgeless](https://upload.wikimedia.org/wikipedia/commons/b/bf/Undirected.svg)
13+
14+
An undirected connected graph with no cut edges
15+
16+
![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge1.png)
17+
18+
![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge2.png)
19+
20+
![Bridges in graph](https://www.geeksforgeeks.org/wp-content/uploads/Bridge3.png)
21+
22+
## References
23+
24+
- [Wikipedia](https://en.wikipedia.org/wiki/Bridge_%28graph_theory%29#Tarjan.27s_Bridge-finding_algorithm)
25+
- [GeeksForGeeks](https://www.geeksforgeeks.org/bridge-in-a-graph/)
26+
- [GeeksForGeeks on YouTube](https://www.youtube.com/watch?time_continue=110&v=thLQYBlz2DM)
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
2+
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
3+
import Graph from '../../../../data-structures/graph/Graph';
4+
import graphBridges from '../graphBridges';
5+
6+
describe('graphBridges', () => {
7+
it('should find bridges in simple graph', () => {
8+
const vertexA = new GraphVertex('A');
9+
const vertexB = new GraphVertex('B');
10+
const vertexC = new GraphVertex('C');
11+
const vertexD = new GraphVertex('D');
12+
13+
const edgeAB = new GraphEdge(vertexA, vertexB);
14+
const edgeBC = new GraphEdge(vertexB, vertexC);
15+
const edgeCD = new GraphEdge(vertexC, vertexD);
16+
17+
const graph = new Graph();
18+
19+
graph
20+
.addEdge(edgeAB)
21+
.addEdge(edgeBC)
22+
.addEdge(edgeCD);
23+
24+
const bridges = graphBridges(graph);
25+
26+
expect(bridges.length).toBe(3);
27+
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
28+
expect(bridges[1].getKey()).toBe(edgeBC.getKey());
29+
expect(bridges[2].getKey()).toBe(edgeAB.getKey());
30+
});
31+
32+
it('should find bridges in simple graph with back edge', () => {
33+
const vertexA = new GraphVertex('A');
34+
const vertexB = new GraphVertex('B');
35+
const vertexC = new GraphVertex('C');
36+
const vertexD = new GraphVertex('D');
37+
38+
const edgeAB = new GraphEdge(vertexA, vertexB);
39+
const edgeBC = new GraphEdge(vertexB, vertexC);
40+
const edgeCD = new GraphEdge(vertexC, vertexD);
41+
const edgeAC = new GraphEdge(vertexA, vertexC);
42+
43+
const graph = new Graph();
44+
45+
graph
46+
.addEdge(edgeAB)
47+
.addEdge(edgeAC)
48+
.addEdge(edgeBC)
49+
.addEdge(edgeCD);
50+
51+
const bridges = graphBridges(graph);
52+
53+
expect(bridges.length).toBe(1);
54+
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
55+
});
56+
57+
it('should find bridges in graph', () => {
58+
const vertexA = new GraphVertex('A');
59+
const vertexB = new GraphVertex('B');
60+
const vertexC = new GraphVertex('C');
61+
const vertexD = new GraphVertex('D');
62+
const vertexE = new GraphVertex('E');
63+
const vertexF = new GraphVertex('F');
64+
const vertexG = new GraphVertex('G');
65+
const vertexH = new GraphVertex('H');
66+
67+
const edgeAB = new GraphEdge(vertexA, vertexB);
68+
const edgeBC = new GraphEdge(vertexB, vertexC);
69+
const edgeAC = new GraphEdge(vertexA, vertexC);
70+
const edgeCD = new GraphEdge(vertexC, vertexD);
71+
const edgeDE = new GraphEdge(vertexD, vertexE);
72+
const edgeEG = new GraphEdge(vertexE, vertexG);
73+
const edgeEF = new GraphEdge(vertexE, vertexF);
74+
const edgeGF = new GraphEdge(vertexG, vertexF);
75+
const edgeFH = new GraphEdge(vertexF, vertexH);
76+
77+
const graph = new Graph();
78+
79+
graph
80+
.addEdge(edgeAB)
81+
.addEdge(edgeBC)
82+
.addEdge(edgeAC)
83+
.addEdge(edgeCD)
84+
.addEdge(edgeDE)
85+
.addEdge(edgeEG)
86+
.addEdge(edgeEF)
87+
.addEdge(edgeGF)
88+
.addEdge(edgeFH);
89+
90+
const bridges = graphBridges(graph);
91+
92+
expect(bridges.length).toBe(3);
93+
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
94+
expect(bridges[1].getKey()).toBe(edgeDE.getKey());
95+
expect(bridges[2].getKey()).toBe(edgeCD.getKey());
96+
});
97+
98+
it('should find bridges in graph starting with different root vertex', () => {
99+
const vertexA = new GraphVertex('A');
100+
const vertexB = new GraphVertex('B');
101+
const vertexC = new GraphVertex('C');
102+
const vertexD = new GraphVertex('D');
103+
const vertexE = new GraphVertex('E');
104+
const vertexF = new GraphVertex('F');
105+
const vertexG = new GraphVertex('G');
106+
const vertexH = new GraphVertex('H');
107+
108+
const edgeAB = new GraphEdge(vertexA, vertexB);
109+
const edgeBC = new GraphEdge(vertexB, vertexC);
110+
const edgeAC = new GraphEdge(vertexA, vertexC);
111+
const edgeCD = new GraphEdge(vertexC, vertexD);
112+
const edgeDE = new GraphEdge(vertexD, vertexE);
113+
const edgeEG = new GraphEdge(vertexE, vertexG);
114+
const edgeEF = new GraphEdge(vertexE, vertexF);
115+
const edgeGF = new GraphEdge(vertexG, vertexF);
116+
const edgeFH = new GraphEdge(vertexF, vertexH);
117+
118+
const graph = new Graph();
119+
120+
graph
121+
.addEdge(edgeDE)
122+
.addEdge(edgeAB)
123+
.addEdge(edgeBC)
124+
.addEdge(edgeAC)
125+
.addEdge(edgeCD)
126+
.addEdge(edgeEG)
127+
.addEdge(edgeEF)
128+
.addEdge(edgeGF)
129+
.addEdge(edgeFH);
130+
131+
const bridges = graphBridges(graph);
132+
133+
expect(bridges.length).toBe(3);
134+
expect(bridges[0].getKey()).toBe(edgeFH.getKey());
135+
expect(bridges[1].getKey()).toBe(edgeDE.getKey());
136+
expect(bridges[2].getKey()).toBe(edgeCD.getKey());
137+
});
138+
139+
it('should find bridges in yet another graph #1', () => {
140+
const vertexA = new GraphVertex('A');
141+
const vertexB = new GraphVertex('B');
142+
const vertexC = new GraphVertex('C');
143+
const vertexD = new GraphVertex('D');
144+
const vertexE = new GraphVertex('E');
145+
146+
const edgeAB = new GraphEdge(vertexA, vertexB);
147+
const edgeAC = new GraphEdge(vertexA, vertexC);
148+
const edgeBC = new GraphEdge(vertexB, vertexC);
149+
const edgeCD = new GraphEdge(vertexC, vertexD);
150+
const edgeDE = new GraphEdge(vertexD, vertexE);
151+
152+
const graph = new Graph();
153+
154+
graph
155+
.addEdge(edgeAB)
156+
.addEdge(edgeAC)
157+
.addEdge(edgeBC)
158+
.addEdge(edgeCD)
159+
.addEdge(edgeDE);
160+
161+
const bridges = graphBridges(graph);
162+
163+
expect(bridges.length).toBe(2);
164+
expect(bridges[0].getKey()).toBe(edgeDE.getKey());
165+
expect(bridges[1].getKey()).toBe(edgeCD.getKey());
166+
});
167+
168+
it('should find bridges in yet another graph #2', () => {
169+
const vertexA = new GraphVertex('A');
170+
const vertexB = new GraphVertex('B');
171+
const vertexC = new GraphVertex('C');
172+
const vertexD = new GraphVertex('D');
173+
const vertexE = new GraphVertex('E');
174+
const vertexF = new GraphVertex('F');
175+
const vertexG = new GraphVertex('G');
176+
177+
const edgeAB = new GraphEdge(vertexA, vertexB);
178+
const edgeAC = new GraphEdge(vertexA, vertexC);
179+
const edgeBC = new GraphEdge(vertexB, vertexC);
180+
const edgeCD = new GraphEdge(vertexC, vertexD);
181+
const edgeCE = new GraphEdge(vertexC, vertexE);
182+
const edgeCF = new GraphEdge(vertexC, vertexF);
183+
const edgeEG = new GraphEdge(vertexE, vertexG);
184+
const edgeFG = new GraphEdge(vertexF, vertexG);
185+
186+
const graph = new Graph();
187+
188+
graph
189+
.addEdge(edgeAB)
190+
.addEdge(edgeAC)
191+
.addEdge(edgeBC)
192+
.addEdge(edgeCD)
193+
.addEdge(edgeCE)
194+
.addEdge(edgeCF)
195+
.addEdge(edgeEG)
196+
.addEdge(edgeFG);
197+
198+
const bridges = graphBridges(graph);
199+
200+
expect(bridges.length).toBe(1);
201+
expect(bridges[0].getKey()).toBe(edgeCD.getKey());
202+
});
203+
});
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import depthFirstSearch from '../depth-first-search/depthFirstSearch';
2+
3+
/**
4+
* Helper class for visited vertex metadata.
5+
*/
6+
class VisitMetadata {
7+
constructor({ discoveryTime, lowDiscoveryTime }) {
8+
this.discoveryTime = discoveryTime;
9+
this.lowDiscoveryTime = lowDiscoveryTime;
10+
}
11+
}
12+
13+
/**
14+
* @param {Graph} graph
15+
* @return {GraphVertex[]}
16+
*/
17+
export default function graphBridges(graph) {
18+
// Set of vertices we've already visited during DFS.
19+
const visitedSet = {};
20+
21+
// Set of bridges.
22+
const bridges = {};
23+
24+
// Time needed to discover to the current vertex.
25+
let discoveryTime = 0;
26+
27+
// Peek the start vertex for DFS traversal.
28+
const startVertex = graph.getAllVertices()[0];
29+
30+
const dfsCallbacks = {
31+
/**
32+
* @param {GraphVertex} currentVertex
33+
*/
34+
enterVertex: ({ currentVertex }) => {
35+
// Tick discovery time.
36+
discoveryTime += 1;
37+
38+
// Put current vertex to visited set.
39+
visitedSet[currentVertex.getKey()] = new VisitMetadata({
40+
discoveryTime,
41+
lowDiscoveryTime: discoveryTime,
42+
});
43+
},
44+
/**
45+
* @param {GraphVertex} currentVertex
46+
* @param {GraphVertex} previousVertex
47+
*/
48+
leaveVertex: ({ currentVertex, previousVertex }) => {
49+
if (previousVertex === null) {
50+
// Don't do anything for the root vertex if it is already current (not previous one).
51+
return;
52+
}
53+
54+
// Check if current node is connected to any early node other then previous one.
55+
visitedSet[currentVertex.getKey()].lowDiscoveryTime = currentVertex.getNeighbors()
56+
.filter(earlyNeighbor => earlyNeighbor.getKey() !== previousVertex.getKey())
57+
.reduce(
58+
/**
59+
* @param {number} lowestDiscoveryTime
60+
* @param {GraphVertex} neighbor
61+
*/
62+
(lowestDiscoveryTime, neighbor) => {
63+
const neighborLowTime = visitedSet[neighbor.getKey()].lowDiscoveryTime;
64+
return neighborLowTime < lowestDiscoveryTime ? neighborLowTime : lowestDiscoveryTime;
65+
},
66+
visitedSet[currentVertex.getKey()].lowDiscoveryTime,
67+
);
68+
69+
// Compare low discovery times. In case if current low discovery time is less than the one
70+
// in previous vertex then update previous vertex low time.
71+
const currentLowDiscoveryTime = visitedSet[currentVertex.getKey()].lowDiscoveryTime;
72+
const previousLowDiscoveryTime = visitedSet[previousVertex.getKey()].lowDiscoveryTime;
73+
if (currentLowDiscoveryTime < previousLowDiscoveryTime) {
74+
visitedSet[previousVertex.getKey()].lowDiscoveryTime = currentLowDiscoveryTime;
75+
}
76+
77+
// Compare current vertex low discovery time with parent discovery time. Check if there
78+
// are any short path (back edge) exists. If we can't get to current vertex other then
79+
// via parent then the parent vertex is articulation point for current one.
80+
const parentDiscoveryTime = visitedSet[previousVertex.getKey()].discoveryTime;
81+
if (parentDiscoveryTime < currentLowDiscoveryTime) {
82+
const bridge = graph.findEdge(previousVertex, currentVertex);
83+
bridges[bridge.getKey()] = bridge;
84+
}
85+
},
86+
allowTraversal: ({ nextVertex }) => {
87+
return !visitedSet[nextVertex.getKey()];
88+
},
89+
};
90+
91+
// Do Depth First Search traversal over submitted graph.
92+
depthFirstSearch(graph, startVertex, dfsCallbacks);
93+
94+
return Object.values(bridges);
95+
}

0 commit comments

Comments
 (0)