Skip to content

Commit

Permalink
chore: merge "feat: add Graph Coloring algorithms (#349)"
Browse files Browse the repository at this point in the history
* feat: add Graph Coloring algorithms

* chore(graph): styling & naming changes
  • Loading branch information
Shivam010 authored Sep 20, 2021
1 parent bcb29db commit 4edb802
Show file tree
Hide file tree
Showing 13 changed files with 489 additions and 3 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
50 changes: 50 additions & 0 deletions graph/coloring/backtracking.go
Original file line number Diff line number Diff line change
@@ -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
}
24 changes: 24 additions & 0 deletions graph/coloring/backtracking_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
56 changes: 56 additions & 0 deletions graph/coloring/bfs.go
Original file line number Diff line number Diff line change
@@ -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
}
24 changes: 24 additions & 0 deletions graph/coloring/bfs_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
5 changes: 5 additions & 0 deletions graph/coloring/doc.go
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions graph/coloring/graph.go
Original file line number Diff line number Diff line change
@@ -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
}
153 changes: 153 additions & 0 deletions graph/coloring/graph_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
})
}
}
Loading

0 comments on commit 4edb802

Please sign in to comment.