From 1378d8ce359f7b488da0af65a142350be205bde7 Mon Sep 17 00:00:00 2001 From: Aniruddha Bhattacharjee Date: Tue, 11 Jan 2022 17:05:30 +0530 Subject: [PATCH] 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 Co-authored-by: Rak Laptudirm --- README.md | 39 ++++++------ graph/articulationpoints.go | 100 +++++++++++++++++++++++++++++++ graph/articulationpoints_test.go | 82 +++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 18 deletions(-) create mode 100644 graph/articulationpoints.go create mode 100644 graph/articulationpoints_test.go diff --git a/README.md b/README.md index 03f8529c5..0743c1981 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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. ---
@@ -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 diff --git a/graph/articulationpoints.go b/graph/articulationpoints.go new file mode 100644 index 000000000..8c295d639 --- /dev/null +++ b/graph/articulationpoints.go @@ -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 + } + + } +} diff --git a/graph/articulationpoints_test.go b/graph/articulationpoints_test.go new file mode 100644 index 000000000..a9dca3e6d --- /dev/null +++ b/graph/articulationpoints_test.go @@ -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) + } + }) + } +}