Repository: TheAlgorithms/Java
Pull request #5034 (2024-01-31 17:19:26)

Add `WelshPowell` (Graph Colouring)

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`

straf10: i try to format based on clang format but i cant. Dowloaded llvm added to enviroment variables and path and then my computer finds the clang format. After that i dont know what to do.
vil02: > i try to format based on clang format but i cant. Dowloaded llvm added to enviroment variables and path and then my computer finds the clang format. After that i dont know what to do.

You can have a look at the [log](https://github.com/TheAlgorithms/Java/actions/runs/7730295451/job/21075414126?pr=5034) and introduce the required changes manually.

If you are having trouble with running `clang-format`, have a look here: https://github.com/TheAlgorithms/Java/pull/4438#issuecomment-1741399556.
straf10: i think i changed everything you asked, please inform me if i need to change or fix anything else.
Thanks in advance.
straf10: i think i resolved almost all of the changes requested. Have a look and let me know if anything else needs to be changed.
straf10: i added the complex test based on the website, add the blank_color method, updated the comments on the precolored test, and followed all your suggestions. Have a look for any minor changes needed but all in all I think it is ready.

Patch:
diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java
new file mode 100644
index 00000000..3b823f02
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java
@@ -0,0 +1,113 @@
+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 static final int BLANK_COLOR = -1; // Representing uncolored state
+
+    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> getAdjacencyList(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 (isBlank(colors[vertex])) {
+                boolean[] usedColors = computeUsedColors(graph, vertex, colors);
+                final var newColor = firstUnusedColor(usedColors);
+                colors[vertex] = newColor;
+                Arrays.stream(sortedVertices).forEach(otherVertex -> {
+                    if (isBlank(colors[otherVertex]) && !isAdjacentToColored(graph, otherVertex, colors)) {
+                        colors[otherVertex] = newColor;
+                    }
+                });
+            }
+        }
+        return colors;
+    }
+
+    private static boolean isBlank(int color) {
+        return color == BLANK_COLOR;
+    }
+
+    private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) {
+        return graph.getAdjacencyList(vertex).stream().anyMatch(otherVertex -> !isBlank(colors[otherVertex]));
+    }
+
+    private static int[] initializeColors(int numberOfVertices) {
+        int[] colors = new int[numberOfVertices];
+        Arrays.fill(colors, BLANK_COLOR);
+        return colors;
+    }
+
+    private static Integer[] getSortedNodes(final Graph graph) {
+        return IntStream.range(0, graph.getNumVertices()).boxed().sorted(Comparator.comparingInt(v -> - graph.getAdjacencyList(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.getAdjacencyList(vertex).stream().map(neighbor -> colors[neighbor]).filter(color -> !isBlank(color)).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();
+    }
+}


Test patch:
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java
new file mode 100644
index 00000000..b37657db
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java
@@ -0,0 +1,124 @@
+package com.thealgorithms.datastructures.graphs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+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));
+    }
+
+    // The following test originates from the following website : https://www.geeksforgeeks.org/welsh-powell-graph-colouring-algorithm/
+    @Test
+    void testComplexGraph() {
+        int[][] edges = {
+            {0, 7}, // A-H
+            {0, 1}, // A-B
+            {1, 3}, // B-D
+            {2, 3}, // C-D
+            {3, 8}, // D-I
+            {3, 10}, // D-K
+            {4, 10}, // E-K
+            {4, 5}, // E-F
+            {5, 6}, // F-G
+            {6, 10}, // G-K
+            {6, 7}, // G-H
+            {7, 8}, // H-I
+            {7, 9}, // H-J
+            {7, 10}, // H-K
+            {8, 9}, // I-J
+            {9, 10}, // J-K
+        };
+
+        final var graph = WelshPowell.makeGraph(11, edges); // 11 vertices from A (0) to K (10)
+        int[] colors = WelshPowell.findColoring(graph);
+
+        assertTrue(isColoringValid(graph, colors), "The coloring should be valid with no adjacent vertices sharing the same color.");
+        assertEquals(3, countDistinctColors(colors), "The chromatic number of the graph should be 3.");
+    }
+
+    @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");
+        assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, -1}}); }, "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() {
+        // Create a linear graph with 4 vertices and edges connecting them in sequence
+        final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}});
+
+        // Apply the Welsh-Powell coloring algorithm to the graph
+        int[] colors = WelshPowell.findColoring(graph);
+
+        // Validate that the coloring is correct (no two adjacent vertices have the same color)
+        assertTrue(isColoringValid(graph, colors));
+
+        // Check if the algorithm has used at least 2 colors (expected for a linear graph)
+        assertTrue(countDistinctColors(colors) >= 2);
+
+        // Verify that all vertices have been assigned a color
+        for (int color : colors) {
+            assertTrue(color >= 0);
+        }
+    }
+
+    private boolean isColoringValid(Graph graph, int[] colors) {
+        if (Arrays.stream(colors).anyMatch(n -> n < 0)) {
+            return false;
+        }
+        for (int i = 0; i < graph.getNumVertices(); i++) {
+            for (int neighbor : graph.getAdjacencyList(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();
+    }
+}