diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index feaaeb162..c3abb3977 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Being a contributor at The Algorithms, we request you to follow the points menti - You did your own work. - No plagiarism is allowed. Any plagiarized work will not be merged. -- Your work will be distributed under the [MIT License](https://github.com/TheAlgoritms/Go/blob/master/LICENSE) once your pull request has been merged. +- Your work will be distributed under the [MIT License](https://github.com/TheAlgorithms/Go/blob/master/LICENSE) once your pull request has been merged. - Please follow the repository guidelines and standards mentioned below. **New implementation** New implementations are welcome! diff --git a/README.md b/README.md index f9ff81a69..e71ab36d9 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,11 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute. * [Breadth First Search](./graph/breadthfirstsearch.go) * [Depth First Search](./graph/depthfirstsearch.go) * [Floyd Warshall](./graph/floydwarshall.go) - +* [Coloring Algorithms](./graph/coloring) + * [Using BFS](./graph/coloring/bfs.go) + * [Using Backtracking](./graph/coloring/backtracking.go) + * [Using Greedy Approach](./graph/coloring/greedy.go) + ### Math * [Gcd](./math/gcd) * [Extended](./math/gcd/extended.go) diff --git a/graph/coloring/backtracking.go b/graph/coloring/backtracking.go new file mode 100644 index 000000000..1ae657d45 --- /dev/null +++ b/graph/coloring/backtracking.go @@ -0,0 +1,50 @@ +// This file contains the graph coloring implementation using backtracking +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring + +// ColorUsingBacktracking will return the Color of each vertex and the +// total number of different colors used, using backtracking +func (g *Graph) ColorUsingBacktracking() (map[int]Color, int) { + vertexColors := make(map[int]Color, g.vertices) + g.colorVertex(0, vertexColors) + + colorsUsed := 0 + for _, cr := range vertexColors { + if colorsUsed < int(cr) { + colorsUsed = int(cr) + } + } + return vertexColors, colorsUsed +} + +// colorVertex will try to color provided vertex, v +func (g *Graph) colorVertex(v int, color map[int]Color) bool { + // If all vertices are colored, the colors store will be completely filled. + if len(color) == g.vertices { + return true + } + + // As the upper bound of no. of colors is the no. of vertices in graph, + // try assigning each color to the vertex v + for cr := Color(1); cr <= Color(g.vertices); cr++ { + // Use the color, cr for vertex, v if it is safe to use, by + // checking its neighbours + safe := true + for nb := range g.edges[v] { + // cr, color is not safe if color of nb, crnb is not equal to cr + if crnb, ok := color[nb]; ok && crnb == cr { + safe = false + break + } + } + if safe { + color[v] = cr + if g.colorVertex(v+1, color) { + return true + } + delete(color, v) + } + } + return false +} diff --git a/graph/coloring/backtracking_test.go b/graph/coloring/backtracking_test.go new file mode 100644 index 000000000..6bc735cca --- /dev/null +++ b/graph/coloring/backtracking_test.go @@ -0,0 +1,24 @@ +// This file provides tests for coloring using backtracking. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring_test + +import ( + "strconv" + "testing" +) + +func TestGraphColorUsingBacktracking(t *testing.T) { + for i, tt := range getTestGraphs() { + t.Run(strconv.Itoa(i), func(t *testing.T) { + colorsOfVertices, colors := tt.Graph.ColorUsingBacktracking() + if colors != tt.ColorsUsed { + t.Errorf("ColorUsingBacktracking() return more number of colors: %v, want %v colors", colors, tt.ColorsUsed) + } + // check colors + if err := tt.Graph.ValidateColorsOfVertex(colorsOfVertices); err != nil { + t.Errorf("ColorUsingBacktracking() assigned colors are wrong, error = %v", err) + } + }) + } +} diff --git a/graph/coloring/bfs.go b/graph/coloring/bfs.go new file mode 100644 index 000000000..d73b0ed31 --- /dev/null +++ b/graph/coloring/bfs.go @@ -0,0 +1,56 @@ +// This file contains the graph coloring implementation using BFS +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring + +import "container/list" + +// ColorUsingBFS will return the Color of each vertex and the +// total number of different colors used, using BFS +func (g *Graph) ColorUsingBFS() (map[int]Color, int) { + // Initially all vertices will have same color + vertexColors := make(map[int]Color, g.vertices) + for i := 0; i < g.vertices; i++ { + vertexColors[i] = 1 + } + + visited := make(map[int]struct{}) + // Run BFS from each non-visited vertex + for i := 0; i < g.vertices; i++ { + if _, ok := visited[i]; ok { + continue + } + visited[i] = struct{}{} + + queue := list.New() + queue.PushBack(i) + + for queue.Len() != 0 { + // front vertex in the queue + frontNode := queue.Front() + front := frontNode.Value.(int) + queue.Remove(frontNode) + + // Now, check all neighbours of front vertex, if they have same + // color as that of front, change their color + for nb := range g.edges[front] { + if vertexColors[nb] == vertexColors[front] { + vertexColors[nb]++ + } + // if the neighbour is not already visited, add it to the queue + if _, ok := visited[nb]; !ok { + visited[nb] = struct{}{} + queue.PushBack(nb) + } + } + } + } + + colorsUsed := 0 + for _, cr := range vertexColors { + if colorsUsed < int(cr) { + colorsUsed = int(cr) + } + } + return vertexColors, colorsUsed +} diff --git a/graph/coloring/bfs_test.go b/graph/coloring/bfs_test.go new file mode 100644 index 000000000..adfc502ce --- /dev/null +++ b/graph/coloring/bfs_test.go @@ -0,0 +1,24 @@ +// This file provides tests for coloring using BFS. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring_test + +import ( + "strconv" + "testing" +) + +func TestGraphColorUsingBFS(t *testing.T) { + for i, tt := range getTestGraphs() { + t.Run(strconv.Itoa(i), func(t *testing.T) { + colorsOfVertices, colors := tt.Graph.ColorUsingBFS() + if colors != tt.ColorsUsed { + t.Errorf("ColorUsingBFS() return more number of colors: %v, want %v colors", colors, tt.ColorsUsed) + } + // check colors + if err := tt.Graph.ValidateColorsOfVertex(colorsOfVertices); err != nil { + t.Errorf("ColorUsingBFS() assigned colors are wrong, error = %v", err) + } + }) + } +} diff --git a/graph/coloring/doc.go b/graph/coloring/doc.go new file mode 100644 index 000000000..849144dcc --- /dev/null +++ b/graph/coloring/doc.go @@ -0,0 +1,5 @@ +// Package coloring provides implementation of different graph coloring +// algorithms, e.g. coloring using BFS, using Backtracking, using greedy +// approach. +// Author(s): [Shivam](https://github.com/Shivam010) +package coloring diff --git a/graph/coloring/graph.go b/graph/coloring/graph.go new file mode 100644 index 000000000..d8065cb10 --- /dev/null +++ b/graph/coloring/graph.go @@ -0,0 +1,57 @@ +// This file contains the simple structural implementation of undirected +// graph, used in coloring algorithms. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring + +import "errors" + +// Color provides a type for vertex color +type Color int + +// Graph provides a structure to store an undirected graph. +// It is safe to use its empty object. +type Graph struct { + vertices int + edges map[int]map[int]struct{} +} + +// AddVertex will add a new vertex in the graph, if the vertex already +// exist it will do nothing +func (g *Graph) AddVertex(v int) { + if g.edges == nil { + g.edges = make(map[int]map[int]struct{}) + } + + // Check if vertex is present or not + if _, ok := g.edges[v]; !ok { + g.vertices++ + g.edges[v] = make(map[int]struct{}) + } +} + +// AddEdge will add a new edge between the provided vertices in the graph +func (g *Graph) AddEdge(one, two int) { + // Add vertices: one and two to the graph if they are not present + g.AddVertex(one) + g.AddVertex(two) + + // and finally add the edges: one->two and two->one for undirected graph + g.edges[one][two] = struct{}{} + g.edges[two][one] = struct{}{} +} + +func (g *Graph) ValidateColorsOfVertex(colors map[int]Color) error { + if g.vertices != len(colors) { + return errors.New("coloring: not all vertices of graph are colored") + } + // check colors + for vertex, neighbours := range g.edges { + for nb := range neighbours { + if colors[vertex] == colors[nb] { + return errors.New("coloring: same colors of neighbouring vertex") + } + } + } + return nil +} diff --git a/graph/coloring/graph_test.go b/graph/coloring/graph_test.go new file mode 100644 index 000000000..839d9f261 --- /dev/null +++ b/graph/coloring/graph_test.go @@ -0,0 +1,153 @@ +// This file provides tests for graph coloring validations. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring_test + +import ( + "github.com/TheAlgorithms/Go/graph/coloring" + "strconv" + "testing" +) + +type testGraph struct { + Graph *coloring.Graph + ColorsUsed int + VertexColors map[int]coloring.Color +} + +func getTestGraphs() (list []*testGraph) { + // Graph 0th: + // 1---2 + // | / \ + // 4---3 0 + // Min number of colors required = 3 + g0 := &testGraph{ + Graph: &coloring.Graph{}, + ColorsUsed: 3, + VertexColors: map[int]coloring.Color{ + 1: 1, 4: 1, 0: 1, + 2: 2, + 3: 3, + }, + } + list = append(list, g0) + g0.Graph.AddEdge(4, 3) + g0.Graph.AddEdge(3, 1) + g0.Graph.AddEdge(3, 2) + g0.Graph.AddEdge(1, 2) + g0.Graph.AddEdge(2, 0) + + // Graph 1st: + // 1---2 + // | / | + // 4---3---0 + // Min number of colors required = 3 + g1 := &testGraph{ + Graph: &coloring.Graph{}, + ColorsUsed: 3, + VertexColors: map[int]coloring.Color{ + 1: 1, 4: 1, 0: 1, + 2: 2, + 3: 3, + }, + } + list = append(list, g1) + g1.Graph.AddEdge(4, 3) + g1.Graph.AddEdge(3, 1) + g1.Graph.AddEdge(3, 2) + g1.Graph.AddEdge(1, 2) + g1.Graph.AddEdge(2, 0) + g1.Graph.AddEdge(3, 0) + + // Graph 2nd: + // 1---2 + // | + // 4---3 0 + // Min number of colors required = 2 + g2 := &testGraph{ + Graph: &coloring.Graph{}, + ColorsUsed: 2, + VertexColors: map[int]coloring.Color{ + 1: 1, 4: 1, 0: 1, + 2: 2, 3: 2, + }, + } + list = append(list, g2) + g2.Graph.AddVertex(0) + g2.Graph.AddEdge(4, 3) + g2.Graph.AddEdge(3, 1) + g2.Graph.AddEdge(1, 2) + + // Graph 3rd: + // 1---2 4 + // | | | + // 0---3 5 + // Min number of colors required = 2 + g3 := &testGraph{ + Graph: &coloring.Graph{}, + ColorsUsed: 2, + VertexColors: map[int]coloring.Color{ + 1: 1, 3: 1, 4: 1, + 0: 2, 2: 2, 5: 2, + }, + } + list = append(list, g3) + g3.Graph.AddEdge(0, 3) + g3.Graph.AddEdge(1, 2) + g3.Graph.AddEdge(1, 0) + g3.Graph.AddEdge(3, 2) + g3.Graph.AddEdge(4, 5) + + // Graph 4th: + // Completely Connected graph of vertex 4 + // Min number of colors required = 2 + g4 := &testGraph{ + Graph: &coloring.Graph{}, + ColorsUsed: 4, + VertexColors: map[int]coloring.Color{ + 0: 1, 1: 2, 2: 3, 4: 4, + }, + } + list = append(list, g4) + for i := 0; i < 4; i++ { + for j := i + 1; j < 4; j++ { + g4.Graph.AddEdge(i, j) + } + } + + return +} + +func TestGraph_ValidateColorsOfVertex(t *testing.T) { + for i, tt := range getTestGraphs() { + t.Run(strconv.Itoa(i), func(t *testing.T) { + if err := tt.Graph.ValidateColorsOfVertex(tt.VertexColors); err != nil { + t.Errorf("ValidateColorsOfVertex() error = %v, wantErr nil", err) + } + }) + } +} + +func getTestGraphsForNegativeTests() (list []*testGraph) { + list = getTestGraphs() + list[0].VertexColors = nil + list[1].VertexColors = map[int]coloring.Color{} + for v := range list[2].VertexColors { + in := len(list[2].VertexColors) - v - 1 + list[2].VertexColors[v] = list[2].VertexColors[in] + } + for v := range list[3].VertexColors { + list[3].VertexColors[v] = 1 + } + return list[:4] +} + +func TestGraphValidateColorsOfVertex_Negative(t *testing.T) { + for i, tt := range getTestGraphsForNegativeTests() { + t.Run(strconv.Itoa(i), func(t *testing.T) { + if err := tt.Graph.ValidateColorsOfVertex(tt.VertexColors); err == nil { + t.Errorf("ValidateColorsOfVertex() error = nil, want some err") + } + }) + } +} diff --git a/graph/coloring/greedy.go b/graph/coloring/greedy.go new file mode 100644 index 000000000..907395d96 --- /dev/null +++ b/graph/coloring/greedy.go @@ -0,0 +1,52 @@ +// This file contains the graph coloring implementation using Greedy Approach. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring + +import "sort" + +// ColorUsingGreedyApproach will return the Color of each vertex and the +// total number of different colors used, using a greedy approach, based on +// the number of edges (or degree) from any vertex. +func (g *Graph) ColorUsingGreedyApproach() (map[int]Color, int) { + degreeOfVertex := make([]struct{ degree, vertex int }, 0, g.vertices) + for v, neighbours := range g.edges { + degreeOfVertex = append(degreeOfVertex, + struct{ degree, vertex int }{ + vertex: v, + degree: len(neighbours), + }, + ) + } + // sort the degreeOfVertex in decreasing order of degrees + sort.Slice(degreeOfVertex, func(i, j int) bool { + return degreeOfVertex[i].degree > degreeOfVertex[j].degree + }) + + vertexColors := make(map[int]Color, g.vertices) + // Start with a color and assign the color to all possible vertices in the degreeOfVertex slice + // and then, re-iterate with new color for all those which are left + for color := 1; color <= g.vertices; color++ { + vertexLoop: + for _, val := range degreeOfVertex { + // skip, if already assigned + if _, ok := vertexColors[val.vertex]; ok { + continue vertexLoop + } + // Check its neighbours + for ng := range g.edges[val.vertex] { + if vertexColors[ng] == Color(color) { + // not possible to use this color for val.vertex + continue vertexLoop + } + } + // Assign color to the vertex + vertexColors[val.vertex] = Color(color) + } + // continue till all the vertices are colored + if len(vertexColors) == g.vertices { + return vertexColors, color + } + } + return vertexColors, g.vertices +} diff --git a/graph/coloring/greedy_test.go b/graph/coloring/greedy_test.go new file mode 100644 index 000000000..cd55d7d38 --- /dev/null +++ b/graph/coloring/greedy_test.go @@ -0,0 +1,24 @@ +// This file provides tests for coloring using Greedy approach. +// Author(s): [Shivam](https://github.com/Shivam010) + +package coloring_test + +import ( + "strconv" + "testing" +) + +func TestGraphColorUsingGreedyApproach(t *testing.T) { + for i, tt := range getTestGraphs() { + t.Run(strconv.Itoa(i), func(t *testing.T) { + colorsOfVertices, colors := tt.Graph.ColorUsingGreedyApproach() + if colors != tt.ColorsUsed { + t.Errorf("ColorUsingGreedyApproach() return more number of colors: %v, want %v colors", colors, tt.ColorsUsed) + } + // check colors + if err := tt.Graph.ValidateColorsOfVertex(colorsOfVertices); err != nil { + t.Errorf("ColorUsingGreedyApproach() assigned colors are wrong, error = %v", err) + } + }) + } +} diff --git a/graph/doc.go b/graph/doc.go index 32a55f68b..ec3d4c5ce 100644 --- a/graph/doc.go +++ b/graph/doc.go @@ -1,3 +1,3 @@ -// Package graph demonstates Graph search algorithms +// Package graph demonstrates Graph search algorithms // reference: https://en.wikipedia.org/wiki/Tree_traversal package graph diff --git a/graph/graph.go b/graph/graph.go new file mode 100644 index 000000000..6d00d1661 --- /dev/null +++ b/graph/graph.go @@ -0,0 +1,37 @@ +// This file contains the simple structural implementation of undirected +// graph. Used in coloring algorithms [here](./coloring) +// Author(s): [Shivam](https://github.com/Shivam010) + +package graph + +// UndirectedGraph provides a structure to store an undirected graph. +// It is safe to use its empty object. +type UndirectedGraph struct { + vertices int + edges map[int]map[int]struct{} +} + +// AddVertex will add a new vertex in the graph, if the vertex already +// exist it will do nothing +func (g *UndirectedGraph) AddVertex(v int) { + if g.edges == nil { + g.edges = make(map[int]map[int]struct{}) + } + + // Check if vertex is present or not + if _, ok := g.edges[v]; !ok { + g.vertices++ + g.edges[v] = make(map[int]struct{}) + } +} + +// AddEdge will add a new edge between the provided vertices in the graph +func (g *UndirectedGraph) AddEdge(one, two int) { + // Add vertices: one and two to the graph if they are not present + g.AddVertex(one) + g.AddVertex(two) + + // and finally add the edges: one->two and two->one for undirected graph + g.edges[one][two] = struct{}{} + g.edges[two][one] = struct{}{} +}