Skip to content

Commit

Permalink
Graphs articulation points (#452)
Browse files Browse the repository at this point in the history
* 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
4 people authored Jan 11, 2022
1 parent 6134d6e commit 1378d8c
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 18 deletions.
39 changes: 21 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,16 +390,17 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
---
##### Functions:

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
2. [`DepthFirstSearch`](./graph/depthfirstsearch.go#L53): No description provided.
3. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided.
4. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
5. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
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.
7. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
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
9. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
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
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
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
3. [`DepthFirstSearch`](./graph/depthfirstsearch.go#L53): No description provided.
4. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided.
5. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
6. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
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.
8. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
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
10. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
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

---
##### Types
Expand Down Expand Up @@ -605,6 +606,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
##### Functions:

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

---
</details><details>
Expand Down Expand Up @@ -836,14 +838,15 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
4. [`HeapSort`](./sort/heapsort.go#L121): No description provided.
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
6. [`InsertionSort`](./sort/insertionsort.go#L3): No description provided.
7. [`Mergesort`](./sort/mergesort.go#L35): Mergesort Perform mergesort on a slice of ints
8. [`Pigeonhole`](./sort/pigeonholesort.go#L12): Pigeonhole sorts a slice using pigeonhole sorting algorithm.
9. [`QuickSort`](./sort/quicksort.go#L37): QuickSort Sorts the entire array
10. [`QuickSortRange`](./sort/quicksort.go#L24): QuickSortRange Sorts the specified range within the array
11. [`RadixSort`](./sort/radixsort.go#L35): No description provided.
12. [`SelectionSort`](./sort/selectionsort.go#L3): No description provided.
13. [`ShellSort`](./sort/shellsort.go#L3): No description provided.
14. [`SimpleSort`](./sort/simplesort.go#L11): No description provided.
7. [`MergeIter`](./sort/mergesort.go#L51): No description provided.
8. [`Mergesort`](./sort/mergesort.go#L37): Mergesort Perform mergesort on a slice of ints
9. [`Pigeonhole`](./sort/pigeonholesort.go#L12): Pigeonhole sorts a slice using pigeonhole sorting algorithm.
10. [`QuickSort`](./sort/quicksort.go#L37): QuickSort Sorts the entire array
11. [`QuickSortRange`](./sort/quicksort.go#L24): QuickSortRange Sorts the specified range within the array
12. [`RadixSort`](./sort/radixsort.go#L35): No description provided.
13. [`SelectionSort`](./sort/selectionsort.go#L3): No description provided.
14. [`ShellSort`](./sort/shellsort.go#L3): No description provided.
15. [`SimpleSort`](./sort/simplesort.go#L11): No description provided.

---
##### Types
Expand Down
100 changes: 100 additions & 0 deletions graph/articulationpoints.go
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
}

}
}
82 changes: 82 additions & 0 deletions graph/articulationpoints_test.go
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)
}
})
}
}

0 comments on commit 1378d8c

Please sign in to comment.