-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Articualtion point initial commit * Refactor the function definitions * Add introduction comments and helpful links * Add test for articulation point algorithm * Inital commit for articulation point test * Correct test cases * Rename files to conform to the naming conventions * Add comments * Updated Documentation in README.md * Remove earlier files * Updated Documentation in README.md * Rename dfs to ArticulationPointHelper to avoid confusion * Updated Documentation in README.md * Make the helper function unexported * Updated Documentation in README.md * Refactor the code to avoid global variables * Updated Documentation in README.md * Refactor to use structs instead function parameters * Updated Documentation in README.md * Restructure comments to compile with project conventions * Updated Documentation in README.md * Linting changes * Reduce comment length and accommodate review comments * Updated Documentation in README.md * Updated Documentation in README.md * Modify function definition to pass graph parameter as a pointer Co-authored-by: github-action <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Taj <[email protected]> Co-authored-by: Rak Laptudirm <[email protected]>
- Loading branch information
1 parent
6134d6e
commit 1378d8c
Showing
3 changed files
with
203 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package graph | ||
|
||
import "github.com/TheAlgorithms/Go/math/min" | ||
|
||
type apHelper struct { | ||
is_ap []bool | ||
visited []bool | ||
child_cnt []int | ||
discovery_time []int | ||
earliest_discovery []int | ||
} | ||
|
||
// ArticulationPoint is a function to identify articulation points in a graph. | ||
// The function takes the graph as an argument and returns a boolean slice | ||
// which indicates whether a vertex is an articulation point or not. | ||
// Worst Case Time Complexity: O(|V| + |E|) | ||
// Auxiliary Space: O(|V|) | ||
// reference: https://en.wikipedia.org/wiki/Biconnected_component and https://cptalks.quora.com/Cut-Vertex-Articulation-point | ||
func ArticulationPoint(graph *Graph) []bool { | ||
// time variable to keep track of the time of discovery_time of a vertex | ||
time := 0 | ||
|
||
//initialize all the variables | ||
apHelperInstance := &apHelper{ | ||
is_ap: make([]bool, graph.vertices), | ||
visited: make([]bool, graph.vertices), | ||
child_cnt: make([]int, graph.vertices), | ||
// integer slice to store the discovery time of a vertex as we traverse | ||
// the graph in a depth first manner | ||
discovery_time: make([]int, graph.vertices), | ||
// integer slice to store the earliest discovered vertex reachable from a vertex | ||
earliest_discovery: make([]int, graph.vertices), | ||
} | ||
articulationPointHelper( | ||
apHelperInstance, | ||
0, | ||
-1, | ||
&time, | ||
graph, | ||
) | ||
|
||
if apHelperInstance.child_cnt[0] == 1 { | ||
// if the root has only one child, it is not an articulation point | ||
apHelperInstance.is_ap[0] = false | ||
} | ||
|
||
return apHelperInstance.is_ap | ||
} | ||
|
||
// articulationPointHelper is a recursive function to traverse the graph | ||
// and mark articulation points. Based on the depth first search transversal | ||
// of the graph, however modified to keep track and update the | ||
// `child_cnt`, `discovery_time`` and `earliest_discovery` slices defined above | ||
func articulationPointHelper( | ||
apHelperInstance *apHelper, | ||
vertex int, | ||
parent int, | ||
time *int, | ||
graph *Graph, | ||
) { | ||
apHelperInstance.visited[vertex] = true | ||
|
||
// Mark the time of discovery of a vertex | ||
// set the earliest discovery time to the discovered time | ||
// increment the time | ||
apHelperInstance.discovery_time[vertex] = *time | ||
apHelperInstance.earliest_discovery[vertex] = apHelperInstance.discovery_time[vertex] | ||
*time++ | ||
|
||
for next_vertex := range graph.edges[vertex] { | ||
if next_vertex == parent { | ||
continue | ||
} | ||
|
||
if apHelperInstance.visited[next_vertex] { | ||
apHelperInstance.earliest_discovery[vertex] = min.Int( | ||
apHelperInstance.earliest_discovery[vertex], | ||
apHelperInstance.discovery_time[next_vertex], | ||
) | ||
continue | ||
} | ||
|
||
apHelperInstance.child_cnt[vertex]++ | ||
articulationPointHelper( | ||
apHelperInstance, | ||
next_vertex, | ||
vertex, | ||
time, | ||
graph, | ||
) | ||
apHelperInstance.earliest_discovery[vertex] = min.Int( | ||
apHelperInstance.earliest_discovery[vertex], | ||
apHelperInstance.earliest_discovery[next_vertex], | ||
) | ||
if apHelperInstance.earliest_discovery[next_vertex] >= apHelperInstance.discovery_time[vertex] { | ||
apHelperInstance.is_ap[vertex] = true | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package graph | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestArticulationPoints(t *testing.T) { | ||
var testCases = []struct { | ||
description string | ||
graph Graph | ||
expected []bool | ||
}{ | ||
{ | ||
"Linear tree structure", | ||
Graph{ | ||
vertices: 5, | ||
edges: map[int]map[int]int{ | ||
0: { | ||
1: 0, | ||
}, | ||
|
||
1: { | ||
0: 0, | ||
2: 0, | ||
}, | ||
|
||
2: { | ||
1: 0, | ||
3: 0, | ||
}, | ||
|
||
3: { | ||
2: 0, | ||
4: 0, | ||
}, | ||
}, | ||
}, | ||
[]bool{false, true, true, true, false}, | ||
}, { | ||
"A complete graph", | ||
Graph{ | ||
vertices: 4, | ||
edges: map[int]map[int]int{ | ||
0: { | ||
1: 0, | ||
2: 0, | ||
3: 0, | ||
}, | ||
|
||
1: { | ||
0: 0, | ||
2: 0, | ||
3: 0, | ||
}, | ||
|
||
2: { | ||
0: 0, | ||
1: 0, | ||
3: 0, | ||
}, | ||
|
||
3: { | ||
0: 0, | ||
1: 0, | ||
2: 0, | ||
}, | ||
}, | ||
}, | ||
[]bool{false, false, false, false}, | ||
}, | ||
} | ||
|
||
for _, test := range testCases { | ||
t.Run(test.description, func(t *testing.T) { | ||
is_ap := ArticulationPoint(&test.graph) | ||
if !reflect.DeepEqual(is_ap, test.expected) { | ||
t.Logf("FAIL: %s", test.description) | ||
} | ||
}) | ||
} | ||
} |