-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: merge "feat: add Graph Coloring algorithms (#349)"
* feat: add Graph Coloring algorithms * chore(graph): styling & naming changes
- Loading branch information
Showing
13 changed files
with
489 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.