diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java index 0981638d4..26ca97736 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java @@ -5,21 +5,41 @@ 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 +/** + * The Welsh-Powell algorithm is a graph coloring algorithm that aims to color a graph + * using the minimum number of colors such that no two adjacent vertices share the same color. + * + *

+ * The algorithm works by: + *

    + *
  1. Sorting the vertices in descending order based on their degrees (number of edges connected).
  2. + *
  3. Iterating through each vertex and assigning it the smallest available color that has not been used by its adjacent vertices.
  4. + *
  5. Coloring adjacent vertices with the same color is avoided.
  6. + *
+ *

+ * + *

+ * For more information, see Graph Coloring. + *

*/ - public final class WelshPowell { - private static final int BLANK_COLOR = -1; // Representing uncolored state + private static final int BLANK_COLOR = -1; // Constant representing an uncolored state private WelshPowell() { } + /** + * Represents a graph using an adjacency list. + */ static final class Graph { - private HashSet[] adjacencyLists; + private final HashSet[] adjacencyLists; + /** + * Initializes a graph with a specified number of vertices. + * + * @param vertices the number of vertices in the graph + * @throws IllegalArgumentException if the number of vertices is negative + */ private Graph(int vertices) { if (vertices < 0) { throw new IllegalArgumentException("Number of vertices cannot be negative"); @@ -29,6 +49,13 @@ public final class WelshPowell { Arrays.setAll(adjacencyLists, i -> new HashSet<>()); } + /** + * Adds an edge between two vertices in the graph. + * + * @param nodeA one end of the edge + * @param nodeB the other end of the edge + * @throws IllegalArgumentException if the vertices are out of bounds or if a self-loop is attempted + */ private void addEdge(int nodeA, int nodeB) { validateVertex(nodeA); validateVertex(nodeB); @@ -39,21 +66,46 @@ public final class WelshPowell { adjacencyLists[nodeB].add(nodeA); } + /** + * Validates that the vertex index is within the bounds of the graph. + * + * @param vertex the index of the vertex to validate + * @throws IllegalArgumentException if the vertex is out of bounds + */ private void validateVertex(int vertex) { if (vertex < 0 || vertex >= getNumVertices()) { throw new IllegalArgumentException("Vertex " + vertex + " is out of bounds"); } } + /** + * Returns the adjacency list for a specific vertex. + * + * @param vertex the index of the vertex + * @return the set of adjacent vertices + */ HashSet getAdjacencyList(int vertex) { return adjacencyLists[vertex]; } + /** + * Returns the number of vertices in the graph. + * + * @return the number of vertices + */ int getNumVertices() { return adjacencyLists.length; } } + /** + * Creates a graph with the specified number of vertices and edges. + * + * @param numberOfVertices the total number of vertices + * @param listOfEdges a 2D array representing edges where each inner array contains two vertex indices + * @return a Graph object representing the created graph + * @throws IllegalArgumentException if the edge array is invalid or vertices are out of bounds + */ public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) { Graph graph = new Graph(numberOfVertices); for (int[] edge : listOfEdges) { @@ -65,6 +117,12 @@ public final class WelshPowell { return graph; } + /** + * Finds the coloring of the given graph using the Welsh-Powell algorithm. + * + * @param graph the input graph to color + * @return an array of integers where each index represents a vertex and the value represents the color assigned + */ public static int[] findColoring(Graph graph) { int[] colors = initializeColors(graph.getNumVertices()); Integer[] sortedVertices = getSortedNodes(graph); @@ -83,30 +141,70 @@ public final class WelshPowell { return colors; } + /** + * Helper method to check if a color is unassigned + * + * @param color the color to check + * @return {@code true} if the color is unassigned, {@code false} otherwise + */ private static boolean isBlank(int color) { return color == BLANK_COLOR; } + /** + * Checks if a vertex has adjacent colored vertices + * + * @param graph the input graph + * @param vertex the vertex to check + * @param colors the array of colors assigned to the vertices + * @return {@code true} if the vertex has adjacent colored vertices, {@code false} otherwise + */ private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) { return graph.getAdjacencyList(vertex).stream().anyMatch(otherVertex -> !isBlank(colors[otherVertex])); } + /** + * Initializes the colors array with blank color + * + * @param numberOfVertices the number of vertices in the graph + * @return an array of integers representing the colors assigned to the vertices + */ private static int[] initializeColors(int numberOfVertices) { int[] colors = new int[numberOfVertices]; Arrays.fill(colors, BLANK_COLOR); return colors; } + /** + * Sorts the vertices by their degree in descending order + * + * @param graph the input graph + * @return an array of integers representing the vertices sorted by degree + */ 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); } + /** + * Computes the colors already used by the adjacent vertices + * + * @param graph the input graph + * @param vertex the vertex to check + * @param colors the array of colors assigned to the vertices + * @return an array of booleans representing the colors used by the adjacent vertices + */ 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; } + /** + * Finds the first unused color + * + * @param usedColors the array of colors used by the adjacent vertices + * @return the first unused color + */ private static int firstUnusedColor(boolean[] usedColors) { return IntStream.range(0, usedColors.length).filter(color -> !usedColors[color]).findFirst().getAsInt(); } diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java index b37657db5..f45c4e10b 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java @@ -34,26 +34,25 @@ class WelshPowellTest { 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 + {0, 7}, + {0, 1}, + {1, 3}, + {2, 3}, + {3, 8}, + {3, 10}, + {4, 10}, + {4, 5}, + {5, 6}, + {6, 10}, + {6, 7}, + {7, 8}, + {7, 9}, + {7, 10}, + {8, 9}, + {9, 10}, }; final var graph = WelshPowell.makeGraph(11, edges); // 11 vertices from A (0) to K (10) @@ -86,24 +85,35 @@ class WelshPowellTest { @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); } } + @Test + void testLargeGraph() { + int[][] edges = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 0}, {6, 7}, {7, 8}, {8, 6}, {9, 10}, {10, 11}, {11, 9}, {12, 13}, {13, 14}, {14, 15}}; + + final var graph = WelshPowell.makeGraph(16, edges); // 16 vertices + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(3, countDistinctColors(colors)); // Expecting a maximum of 3 colors + } + + @Test + void testStarGraph() { + int[][] edges = {{0, 1}, {0, 2}, {0, 3}, {0, 4}}; + + final var graph = WelshPowell.makeGraph(5, edges); // 5 vertices in a star formation + int[] colors = WelshPowell.findColoring(graph); + assertTrue(isColoringValid(graph, colors)); + assertEquals(2, countDistinctColors(colors)); // Star graph can be colored with 2 colors + } + private boolean isColoringValid(Graph graph, int[] colors) { if (Arrays.stream(colors).anyMatch(n -> n < 0)) { return false;