@@ -7,11 +7,6 @@ class VisitMetadata {
77 constructor ( { discoveryTime, lowDiscoveryTime } ) {
88 this . discoveryTime = discoveryTime ;
99 this . lowDiscoveryTime = lowDiscoveryTime ;
10-
11- // We need this to know to which vertex we need to compare discovery time
12- // when leaving the vertex.
13- this . childVertex = null ;
14-
1510 // We need this in order to check graph root node, whether it has two
1611 // disconnected children or not.
1712 this . independantChildrenCount = 0 ;
@@ -28,10 +23,10 @@ export default function articulationPoints(graph) {
2823 // Set of vertices we've already visited during DFS.
2924 const visitedSet = { } ;
3025
31- // Set of articulation points found so far .
32- const articulationPointsSet = [ ] ;
26+ // Set of articulation points.
27+ const articulationPointsSet = { } ;
3328
34- // Time needed to get to the current vertex.
29+ // Time needed to discover to the current vertex.
3530 let discoveryTime = 0 ;
3631
3732 // Peek the start vertex for DFS traversal.
@@ -53,9 +48,6 @@ export default function articulationPoints(graph) {
5348 discoveryTime += 1 ;
5449
5550 if ( previousVertex ) {
56- // Update child vertex information for previous vertex.
57- visitedSet [ previousVertex . getKey ( ) ] . childVertex = currentVertex ;
58-
5951 // Update children counter for previous vertex.
6052 visitedSet [ previousVertex . getKey ( ) ] . independantChildrenCount += 1 ;
6153 }
@@ -64,44 +56,43 @@ export default function articulationPoints(graph) {
6456 * @param {GraphVertex } currentVertex
6557 * @param {GraphVertex } previousVertex
6658 */
67- leaveVertex : ( { currentVertex } ) => {
68- // Detect whether current vertex is articulation point or not.
69- // To do so we need to check two (OR) conditions:
59+ leaveVertex : ( { currentVertex, previousVertex } ) => {
60+ if ( previousVertex === null ) {
61+ // Don't do anything for the root vertex if it is already current (not previous one)
62+ return ;
63+ }
64+
65+ // Update the low time with the smallest time of adjacent vertices.
66+ // Get minimum low discovery time from all neighbors.
67+ /** @param {GraphVertex } neighbor */
68+ visitedSet [ currentVertex . getKey ( ) ] . lowDiscoveryTime = currentVertex . getNeighbors ( ) . reduce (
69+ ( lowestDiscoveryTime , neighbor ) => {
70+ const neighborLowTime = visitedSet [ neighbor . getKey ( ) ] . lowDiscoveryTime ;
71+ return neighborLowTime < lowestDiscoveryTime ? neighborLowTime : lowestDiscoveryTime ;
72+ } ,
73+ visitedSet [ currentVertex . getKey ( ) ] . lowDiscoveryTime ,
74+ ) ;
75+
76+ // Detect whether previous vertex is articulation point or not.
77+ // To do so we need to check two [OR] conditions:
7078 // 1. Is it a root vertex with at least two independent children.
7179 // 2. If its visited time is <= low time of adjacent vertex.
72- if ( currentVertex === startVertex ) {
73- // Check that it has at least two independent children.
74- if ( visitedSet [ currentVertex . getKey ( ) ] . independantChildrenCount >= 2 ) {
75- articulationPointsSet . push ( currentVertex ) ;
80+ if ( previousVertex === startVertex ) {
81+ // Check that root vertex has at least two independent children.
82+ if ( visitedSet [ previousVertex . getKey ( ) ] . independantChildrenCount >= 2 ) {
83+ articulationPointsSet [ previousVertex . getKey ( ) ] = previousVertex ;
7684 }
7785 } else {
78- // Get child vertex low discovery time.
79- let childVertexLowDiscoveryTime = null ;
80- if ( visitedSet [ currentVertex . getKey ( ) ] . childVertex ) {
81- const childVertexKey = visitedSet [ currentVertex . getKey ( ) ] . childVertex . getKey ( ) ;
82- childVertexLowDiscoveryTime = visitedSet [ childVertexKey ] . lowDiscoveryTime ;
86+ // Get current vertex low discovery time.
87+ const currentLowDiscoveryTime = visitedSet [ currentVertex . getKey ( ) ] . lowDiscoveryTime ;
88+
89+ // Compare current vertex low discovery time with parent discovery time. Check if there
90+ // are any short path (back edge) exists. If we can't get to current vertex other then
91+ // via parent then the parent vertex is articulation point for current one.
92+ const parentDiscoveryTime = visitedSet [ previousVertex . getKey ( ) ] . discoveryTime ;
93+ if ( parentDiscoveryTime <= currentLowDiscoveryTime ) {
94+ articulationPointsSet [ previousVertex . getKey ( ) ] = previousVertex ;
8395 }
84-
85- // Compare child vertex low discovery time with current discovery time to if there
86- // are any short path (back edge) exists. If we can get to child vertex faster then
87- // to current one it means that there is a back edge exists (short path) and current
88- // vertex isn't articulation point.
89- const currentDiscoveryTime = visitedSet [ currentVertex . getKey ( ) ] . discoveryTime ;
90- if ( currentDiscoveryTime <= childVertexLowDiscoveryTime ) {
91- articulationPointsSet . push ( currentVertex ) ;
92- }
93-
94- // Update the low time with the smallest time of adjacent vertices.
95-
96- // Get minimum low discovery time from all neighbors.
97- /** @param {GraphVertex } neighbor */
98- visitedSet [ currentVertex . getKey ( ) ] . lowDiscoveryTime = currentVertex . getNeighbors ( ) . reduce (
99- ( lowestDiscoveryTime , neighbor ) => {
100- const neighborLowTime = visitedSet [ neighbor . getKey ( ) ] . lowDiscoveryTime ;
101- return neighborLowTime < lowestDiscoveryTime ? neighborLowTime : lowestDiscoveryTime ;
102- } ,
103- visitedSet [ currentVertex . getKey ( ) ] . lowDiscoveryTime ,
104- ) ;
10596 }
10697 } ,
10798 allowTraversal : ( { nextVertex } ) => {
@@ -112,5 +103,5 @@ export default function articulationPoints(graph) {
112103 // Do Depth First Search traversal over submitted graph.
113104 depthFirstSearch ( graph , startVertex , dfsCallbacks ) ;
114105
115- return articulationPointsSet ;
106+ return Object . values ( articulationPointsSet ) ;
116107}
0 commit comments