Skip to content

Commit 1378d8c

Browse files
anirudnitsgithub-actiontjgurwara99raklaptudirm
authored
Graphs articulation points (#452)
* 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]>
1 parent 6134d6e commit 1378d8c

File tree

3 files changed

+203
-18
lines changed

3 files changed

+203
-18
lines changed

README.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -390,16 +390,17 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
390390
---
391391
##### Functions:
392392

393-
1. [`BreadthFirstSearch`](./graph/breadthfirstsearch.go#L9): BreadthFirstSearch is an algorithm for traversing and searching graph data structures. It starts at an arbitrary node of a graph, and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level. Worst-case performance O(|V|+|E|)=O(b^{d})}O(|V|+|E|)=O(b^{d}) Worst-case space complexity O(|V|)=O(b^{d})}O(|V|)=O(b^{d}) reference: https://en.wikipedia.org/wiki/Breadth-first_search
394-
2. [`DepthFirstSearch`](./graph/depthfirstsearch.go#L53): No description provided.
395-
3. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided.
396-
4. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
397-
5. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
398-
6. [`KruskalMST`](./graph/kruskal.go#L87): KruskalMST will return a minimum spanning tree along with its total cost to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is the number of edges in the graph and n is number of nodes in it.
399-
7. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
400-
8. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made
401-
9. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
402-
10. [`Topological`](./graph/topological.go#L7): Assumes that graph given is valid and possible to get a topo ordering. constraints are array of []int{a, b}, representing an edge going from a to b
393+
1. [`ArticulationPoint`](./graph/articulationpoints.go#L19): 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
394+
2. [`BreadthFirstSearch`](./graph/breadthfirstsearch.go#L9): BreadthFirstSearch is an algorithm for traversing and searching graph data structures. It starts at an arbitrary node of a graph, and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level. Worst-case performance O(|V|+|E|)=O(b^{d})}O(|V|+|E|)=O(b^{d}) Worst-case space complexity O(|V|)=O(b^{d})}O(|V|)=O(b^{d}) reference: https://en.wikipedia.org/wiki/Breadth-first_search
395+
3. [`DepthFirstSearch`](./graph/depthfirstsearch.go#L53): No description provided.
396+
4. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided.
397+
5. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
398+
6. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
399+
7. [`KruskalMST`](./graph/kruskal.go#L87): KruskalMST will return a minimum spanning tree along with its total cost to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is the number of edges in the graph and n is number of nodes in it.
400+
8. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
401+
9. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made
402+
10. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
403+
11. [`Topological`](./graph/topological.go#L7): Assumes that graph given is valid and possible to get a topo ordering. constraints are array of []int{a, b}, representing an edge going from a to b
403404

404405
---
405406
##### Types
@@ -605,6 +606,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
605606
##### Functions:
606607

607608
1. [`IsPalindrome`](./strings/palindrome/ispalindrome.go#L26): No description provided.
609+
2. [`IsPalindromeRecursive`](./strings/palindrome/ispalindrome.go#L39): No description provided.
608610

609611
---
610612
</details><details>
@@ -836,14 +838,15 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
836838
4. [`HeapSort`](./sort/heapsort.go#L121): No description provided.
837839
5. [`ImprovedSimpleSort`](./sort/simplesort.go#L25): ImprovedSimpleSort is a improve SimpleSort by skipping an unnecessary comparison of the first and last. This improved version is more similar to implementation of insertion sort
838840
6. [`InsertionSort`](./sort/insertionsort.go#L3): No description provided.
839-
7. [`Mergesort`](./sort/mergesort.go#L35): Mergesort Perform mergesort on a slice of ints
840-
8. [`Pigeonhole`](./sort/pigeonholesort.go#L12): Pigeonhole sorts a slice using pigeonhole sorting algorithm.
841-
9. [`QuickSort`](./sort/quicksort.go#L37): QuickSort Sorts the entire array
842-
10. [`QuickSortRange`](./sort/quicksort.go#L24): QuickSortRange Sorts the specified range within the array
843-
11. [`RadixSort`](./sort/radixsort.go#L35): No description provided.
844-
12. [`SelectionSort`](./sort/selectionsort.go#L3): No description provided.
845-
13. [`ShellSort`](./sort/shellsort.go#L3): No description provided.
846-
14. [`SimpleSort`](./sort/simplesort.go#L11): No description provided.
841+
7. [`MergeIter`](./sort/mergesort.go#L51): No description provided.
842+
8. [`Mergesort`](./sort/mergesort.go#L37): Mergesort Perform mergesort on a slice of ints
843+
9. [`Pigeonhole`](./sort/pigeonholesort.go#L12): Pigeonhole sorts a slice using pigeonhole sorting algorithm.
844+
10. [`QuickSort`](./sort/quicksort.go#L37): QuickSort Sorts the entire array
845+
11. [`QuickSortRange`](./sort/quicksort.go#L24): QuickSortRange Sorts the specified range within the array
846+
12. [`RadixSort`](./sort/radixsort.go#L35): No description provided.
847+
13. [`SelectionSort`](./sort/selectionsort.go#L3): No description provided.
848+
14. [`ShellSort`](./sort/shellsort.go#L3): No description provided.
849+
15. [`SimpleSort`](./sort/simplesort.go#L11): No description provided.
847850

848851
---
849852
##### Types

graph/articulationpoints.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package graph
2+
3+
import "github.com/TheAlgorithms/Go/math/min"
4+
5+
type apHelper struct {
6+
is_ap []bool
7+
visited []bool
8+
child_cnt []int
9+
discovery_time []int
10+
earliest_discovery []int
11+
}
12+
13+
// ArticulationPoint is a function to identify articulation points in a graph.
14+
// The function takes the graph as an argument and returns a boolean slice
15+
// which indicates whether a vertex is an articulation point or not.
16+
// Worst Case Time Complexity: O(|V| + |E|)
17+
// Auxiliary Space: O(|V|)
18+
// reference: https://en.wikipedia.org/wiki/Biconnected_component and https://cptalks.quora.com/Cut-Vertex-Articulation-point
19+
func ArticulationPoint(graph *Graph) []bool {
20+
// time variable to keep track of the time of discovery_time of a vertex
21+
time := 0
22+
23+
//initialize all the variables
24+
apHelperInstance := &apHelper{
25+
is_ap: make([]bool, graph.vertices),
26+
visited: make([]bool, graph.vertices),
27+
child_cnt: make([]int, graph.vertices),
28+
// integer slice to store the discovery time of a vertex as we traverse
29+
// the graph in a depth first manner
30+
discovery_time: make([]int, graph.vertices),
31+
// integer slice to store the earliest discovered vertex reachable from a vertex
32+
earliest_discovery: make([]int, graph.vertices),
33+
}
34+
articulationPointHelper(
35+
apHelperInstance,
36+
0,
37+
-1,
38+
&time,
39+
graph,
40+
)
41+
42+
if apHelperInstance.child_cnt[0] == 1 {
43+
// if the root has only one child, it is not an articulation point
44+
apHelperInstance.is_ap[0] = false
45+
}
46+
47+
return apHelperInstance.is_ap
48+
}
49+
50+
// articulationPointHelper is a recursive function to traverse the graph
51+
// and mark articulation points. Based on the depth first search transversal
52+
// of the graph, however modified to keep track and update the
53+
// `child_cnt`, `discovery_time`` and `earliest_discovery` slices defined above
54+
func articulationPointHelper(
55+
apHelperInstance *apHelper,
56+
vertex int,
57+
parent int,
58+
time *int,
59+
graph *Graph,
60+
) {
61+
apHelperInstance.visited[vertex] = true
62+
63+
// Mark the time of discovery of a vertex
64+
// set the earliest discovery time to the discovered time
65+
// increment the time
66+
apHelperInstance.discovery_time[vertex] = *time
67+
apHelperInstance.earliest_discovery[vertex] = apHelperInstance.discovery_time[vertex]
68+
*time++
69+
70+
for next_vertex := range graph.edges[vertex] {
71+
if next_vertex == parent {
72+
continue
73+
}
74+
75+
if apHelperInstance.visited[next_vertex] {
76+
apHelperInstance.earliest_discovery[vertex] = min.Int(
77+
apHelperInstance.earliest_discovery[vertex],
78+
apHelperInstance.discovery_time[next_vertex],
79+
)
80+
continue
81+
}
82+
83+
apHelperInstance.child_cnt[vertex]++
84+
articulationPointHelper(
85+
apHelperInstance,
86+
next_vertex,
87+
vertex,
88+
time,
89+
graph,
90+
)
91+
apHelperInstance.earliest_discovery[vertex] = min.Int(
92+
apHelperInstance.earliest_discovery[vertex],
93+
apHelperInstance.earliest_discovery[next_vertex],
94+
)
95+
if apHelperInstance.earliest_discovery[next_vertex] >= apHelperInstance.discovery_time[vertex] {
96+
apHelperInstance.is_ap[vertex] = true
97+
}
98+
99+
}
100+
}

graph/articulationpoints_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package graph
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestArticulationPoints(t *testing.T) {
9+
var testCases = []struct {
10+
description string
11+
graph Graph
12+
expected []bool
13+
}{
14+
{
15+
"Linear tree structure",
16+
Graph{
17+
vertices: 5,
18+
edges: map[int]map[int]int{
19+
0: {
20+
1: 0,
21+
},
22+
23+
1: {
24+
0: 0,
25+
2: 0,
26+
},
27+
28+
2: {
29+
1: 0,
30+
3: 0,
31+
},
32+
33+
3: {
34+
2: 0,
35+
4: 0,
36+
},
37+
},
38+
},
39+
[]bool{false, true, true, true, false},
40+
}, {
41+
"A complete graph",
42+
Graph{
43+
vertices: 4,
44+
edges: map[int]map[int]int{
45+
0: {
46+
1: 0,
47+
2: 0,
48+
3: 0,
49+
},
50+
51+
1: {
52+
0: 0,
53+
2: 0,
54+
3: 0,
55+
},
56+
57+
2: {
58+
0: 0,
59+
1: 0,
60+
3: 0,
61+
},
62+
63+
3: {
64+
0: 0,
65+
1: 0,
66+
2: 0,
67+
},
68+
},
69+
},
70+
[]bool{false, false, false, false},
71+
},
72+
}
73+
74+
for _, test := range testCases {
75+
t.Run(test.description, func(t *testing.T) {
76+
is_ap := ArticulationPoint(&test.graph)
77+
if !reflect.DeepEqual(is_ap, test.expected) {
78+
t.Logf("FAIL: %s", test.description)
79+
}
80+
})
81+
}
82+
}

0 commit comments

Comments
 (0)