Skip to content

Add WelshPowell (Graph Colouring) #5034

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e230856
Welsh Powell Algorithm + Test
straf10 Jan 31, 2024
080904b
Welsh Powell Algorithm+Test second commit to fit pull request
straf10 Jan 31, 2024
47b77fd
Welsh Powell Algorithm+Test second commit to fit pull request
straf10 Jan 31, 2024
60e2d10
3rd commit
straf10 Jan 31, 2024
5868035
4th commit
straf10 Jan 31, 2024
30c4cdc
5th attempt
straf10 Jan 31, 2024
6200b82
6th attempt
straf10 Jan 31, 2024
037fc3a
7th attempt
straf10 Jan 31, 2024
da6d43a
Update src/main/java/com/thealgorithms/datastructures/graphs/Welsh_Po…
straf10 Jan 31, 2024
7328b1b
8th attempt
straf10 Jan 31, 2024
86f4ecd
Merge remote-tracking branch 'origin/master'
straf10 Jan 31, 2024
c1ae53a
9th commit(removed main and changed the test class)
straf10 Jan 31, 2024
b3e036f
Merge branch 'master' into master
straf10 Feb 1, 2024
a7f3fcd
Update src/main/java/com/thealgorithms/datastructures/graphs/WelshPow…
straf10 Feb 2, 2024
f59fb89
implemented Hashset, changed name to src,dst to nodeA,B added some me…
straf10 Feb 4, 2024
4039a2f
implemented Hashset, changed name to src,dst to nodeA,B added some me…
straf10 Feb 4, 2024
68cefa1
clang format
straf10 Feb 4, 2024
ba19ccb
added 4 new methods to improve the code, test class still unchanged b…
straf10 Feb 4, 2024
207bb9f
implemented the new changes. Test class probably needs more work
straf10 Feb 5, 2024
01db1c3
changed the addEdge method to disallow self loops(nodeA==nodeB), the …
straf10 Feb 6, 2024
a5e876f
added all the tests except one
straf10 Feb 6, 2024
c71082a
preColored test
straf10 Feb 6, 2024
016b561
reverted the findColoring
straf10 Feb 9, 2024
a55e7eb
added more complex test, blank color method
straf10 Feb 12, 2024
90288e7
refactor: use `isBlank`
vil02 Feb 12, 2024
863c4dc
Merge branch 'master' into master
vil02 Feb 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.thealgorithms.datastructures.graphs;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.stream.IntStream;

/*
* The Welsh-Powell algorithm is a graph coloring algorithm
* used for coloring a graph with the minimum number of colors.
* https://en.wikipedia.org/wiki/Graph_coloring
*/

public final class WelshPowell {
private WelshPowell() {
}

static class Graph {
private HashSet<Integer>[] adjacencyLists;

private Graph(int vertices) {
if (vertices < 0) {
throw new IllegalArgumentException("Number of vertices cannot be negative");
}

adjacencyLists = new HashSet[vertices];
Arrays.setAll(adjacencyLists, i -> new HashSet<>());
}

private void addEdge(int nodeA, int nodeB) {
validateVertex(nodeA);
validateVertex(nodeB);
if (nodeA == nodeB) {
throw new IllegalArgumentException("Self-loops are not allowed");
}
adjacencyLists[nodeA].add(nodeB);
adjacencyLists[nodeB].add(nodeA);
}

private void validateVertex(int vertex) {
if (vertex < 0 || vertex >= getNumVertices()) {
throw new IllegalArgumentException("Vertex " + vertex + " is out of bounds");
}
}

HashSet<Integer> getAdjList(int vertex) {
return adjacencyLists[vertex];
}

int getNumVertices() {
return adjacencyLists.length;
}
}

public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) {
Graph graph = new Graph(numberOfVertices);
for (int[] edge : listOfEdges) {
if (edge.length != 2) {
throw new IllegalArgumentException("Edge array must have exactly two elements");
}
graph.addEdge(edge[0], edge[1]);
}
return graph;
}

public static int[] findColoring(Graph graph) {
int[] colors = initializeColors(graph.getNumVertices());
Integer[] sortedVertices = getSortedNodes(graph);
for (int vertex : sortedVertices) {
if (colors[vertex] == -1) {
boolean[] usedColors = computeUsedColors(graph, vertex, colors);
final var newColor = firstUnusedColor(usedColors);
colors[vertex] = newColor;
Arrays.stream(sortedVertices).forEach(otherVertex -> {
if (colors[otherVertex] == -1 && !isAdjacentToColored(graph, otherVertex, colors)) {
colors[otherVertex] = newColor;
}
});
}
}
return colors;
}

private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) {
return graph.getAdjList(vertex).stream().anyMatch(otherVertex -> colors[otherVertex] != -1);
}

private static int[] initializeColors(int numberOfVerticies) {
int[] colors = new int[numberOfVerticies];
Arrays.fill(colors, -1);
return colors;
}

private static Integer[] getSortedNodes(final Graph graph) {
return IntStream.range(0, graph.getNumVertices()).boxed().sorted(Comparator.comparingInt(v -> - graph.getAdjList(v).size())).toArray(Integer[] ::new);
}

private static boolean[] computeUsedColors(final Graph graph, final int vertex, final int[] colors) {
boolean[] usedColors = new boolean[graph.getNumVertices()];
graph.getAdjList(vertex).stream().map(neighbor -> colors[neighbor]).filter(color -> color != -1).forEach(color -> usedColors[color] = true);
return usedColors;
}

private static int firstUnusedColor(boolean[] usedColors) {
return IntStream.range(0, usedColors.length).filter(color -> !usedColors[color]).findFirst().getAsInt();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.thealgorithms.datastructures.graphs;

import static org.junit.jupiter.api.Assertions.*;

import com.thealgorithms.datastructures.graphs.WelshPowell.Graph;
import java.util.Arrays;
import org.junit.jupiter.api.Test;

class WelshPowellTest {

@Test
void testSimpleGraph() {
final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}});
int[] colors = WelshPowell.findColoring(graph);
assertTrue(isColoringValid(graph, colors));
assertEquals(2, countDistinctColors(colors));
}

@Test
void testDisconnectedGraph() {
final var graph = WelshPowell.makeGraph(3, new int[][] {}); // No edges
int[] colors = WelshPowell.findColoring(graph);
assertTrue(isColoringValid(graph, colors));
assertEquals(1, countDistinctColors(colors));
}

@Test
void testCompleteGraph() {
final var graph = WelshPowell.makeGraph(3, new int[][] {{0, 1}, {1, 2}, {2, 0}});
int[] colors = WelshPowell.findColoring(graph);
assertTrue(isColoringValid(graph, colors));
assertEquals(3, countDistinctColors(colors));
}

@Test
void testComplexGraph() {
final var graph = WelshPowell.makeGraph(5, new int[][] {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}, {1, 3}});
int[] colors = WelshPowell.findColoring(graph);
assertTrue(isColoringValid(graph, colors));
assertEquals(3, countDistinctColors(colors)); // Expect exactly 3 colors
}

@Test
void testNegativeVertices() {
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(-1, new int[][] {}); }, "Number of vertices cannot be negative");
}

@Test
void testSelfLoop() {
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 0}}); }, "Self-loops are not allowed");
}

@Test
void testInvalidVertex() {
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 3}}); }, "Vertex out of bounds");
}

@Test
void testInvalidEdgeArray() {
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0}}); }, "Edge array must have exactly two elements");
}

@Test
void testWithPreColoredVertex() {
final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}});
// Simulate pre-coloring vertex 1 with color 0
int[] colors = WelshPowell.findColoring(graph);
assertTrue(isColoringValid(graph, colors));

// Ensure that the pre-colored vertex retains its color
assertEquals(0, colors[1]);

// Check if other vertices are colored correctly
assertTrue(countDistinctColors(colors) >= 2);

// Additional check to ensure that all vertices are colored
for (int color : colors) {
assertTrue(color >= 0);
}
}

private boolean isColoringValid(Graph graph, int[] colors) {
for (int i = 0; i < graph.getNumVertices(); i++) {
for (int neighbor : graph.getAdjList(i)) {
if (i != neighbor && colors[i] == colors[neighbor]) {
return false; // Adjacent vertices have the same color
}
}
}
return true; // No adjacent vertices share the same color
}

private int countDistinctColors(int[] colors) {
return (int) Arrays.stream(colors).distinct().count();
}
}