Skip to content

Commit 474e0de

Browse files
authored
Enhance docs, add more tests in WelshPowell (#5971)
1 parent a78b15d commit 474e0de

File tree

2 files changed

+142
-34
lines changed

2 files changed

+142
-34
lines changed

src/main/java/com/thealgorithms/datastructures/graphs/WelshPowell.java

+106-8
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,41 @@
55
import java.util.HashSet;
66
import java.util.stream.IntStream;
77

8-
/*
9-
* The Welsh-Powell algorithm is a graph coloring algorithm
10-
* used for coloring a graph with the minimum number of colors.
11-
* https://en.wikipedia.org/wiki/Graph_coloring
8+
/**
9+
* The Welsh-Powell algorithm is a graph coloring algorithm that aims to color a graph
10+
* using the minimum number of colors such that no two adjacent vertices share the same color.
11+
*
12+
* <p>
13+
* The algorithm works by:
14+
* <ol>
15+
* <li>Sorting the vertices in descending order based on their degrees (number of edges connected).</li>
16+
* <li>Iterating through each vertex and assigning it the smallest available color that has not been used by its adjacent vertices.</li>
17+
* <li>Coloring adjacent vertices with the same color is avoided.</li>
18+
* </ol>
19+
* </p>
20+
*
21+
* <p>
22+
* For more information, see <a href="https://en.wikipedia.org/wiki/Graph_coloring">Graph Coloring</a>.
23+
* </p>
1224
*/
13-
1425
public final class WelshPowell {
15-
private static final int BLANK_COLOR = -1; // Representing uncolored state
26+
private static final int BLANK_COLOR = -1; // Constant representing an uncolored state
1627

1728
private WelshPowell() {
1829
}
1930

31+
/**
32+
* Represents a graph using an adjacency list.
33+
*/
2034
static final class Graph {
21-
private HashSet<Integer>[] adjacencyLists;
22-
35+
private final HashSet<Integer>[] adjacencyLists;
36+
37+
/**
38+
* Initializes a graph with a specified number of vertices.
39+
*
40+
* @param vertices the number of vertices in the graph
41+
* @throws IllegalArgumentException if the number of vertices is negative
42+
*/
2343
private Graph(int vertices) {
2444
if (vertices < 0) {
2545
throw new IllegalArgumentException("Number of vertices cannot be negative");
@@ -29,6 +49,13 @@ private Graph(int vertices) {
2949
Arrays.setAll(adjacencyLists, i -> new HashSet<>());
3050
}
3151

52+
/**
53+
* Adds an edge between two vertices in the graph.
54+
*
55+
* @param nodeA one end of the edge
56+
* @param nodeB the other end of the edge
57+
* @throws IllegalArgumentException if the vertices are out of bounds or if a self-loop is attempted
58+
*/
3259
private void addEdge(int nodeA, int nodeB) {
3360
validateVertex(nodeA);
3461
validateVertex(nodeB);
@@ -39,21 +66,46 @@ private void addEdge(int nodeA, int nodeB) {
3966
adjacencyLists[nodeB].add(nodeA);
4067
}
4168

69+
/**
70+
* Validates that the vertex index is within the bounds of the graph.
71+
*
72+
* @param vertex the index of the vertex to validate
73+
* @throws IllegalArgumentException if the vertex is out of bounds
74+
*/
4275
private void validateVertex(int vertex) {
4376
if (vertex < 0 || vertex >= getNumVertices()) {
4477
throw new IllegalArgumentException("Vertex " + vertex + " is out of bounds");
4578
}
4679
}
4780

81+
/**
82+
* Returns the adjacency list for a specific vertex.
83+
*
84+
* @param vertex the index of the vertex
85+
* @return the set of adjacent vertices
86+
*/
4887
HashSet<Integer> getAdjacencyList(int vertex) {
4988
return adjacencyLists[vertex];
5089
}
5190

91+
/**
92+
* Returns the number of vertices in the graph.
93+
*
94+
* @return the number of vertices
95+
*/
5296
int getNumVertices() {
5397
return adjacencyLists.length;
5498
}
5599
}
56100

101+
/**
102+
* Creates a graph with the specified number of vertices and edges.
103+
*
104+
* @param numberOfVertices the total number of vertices
105+
* @param listOfEdges a 2D array representing edges where each inner array contains two vertex indices
106+
* @return a Graph object representing the created graph
107+
* @throws IllegalArgumentException if the edge array is invalid or vertices are out of bounds
108+
*/
57109
public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) {
58110
Graph graph = new Graph(numberOfVertices);
59111
for (int[] edge : listOfEdges) {
@@ -65,6 +117,12 @@ public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) {
65117
return graph;
66118
}
67119

120+
/**
121+
* Finds the coloring of the given graph using the Welsh-Powell algorithm.
122+
*
123+
* @param graph the input graph to color
124+
* @return an array of integers where each index represents a vertex and the value represents the color assigned
125+
*/
68126
public static int[] findColoring(Graph graph) {
69127
int[] colors = initializeColors(graph.getNumVertices());
70128
Integer[] sortedVertices = getSortedNodes(graph);
@@ -83,30 +141,70 @@ public static int[] findColoring(Graph graph) {
83141
return colors;
84142
}
85143

144+
/**
145+
* Helper method to check if a color is unassigned
146+
*
147+
* @param color the color to check
148+
* @return {@code true} if the color is unassigned, {@code false} otherwise
149+
*/
86150
private static boolean isBlank(int color) {
87151
return color == BLANK_COLOR;
88152
}
89153

154+
/**
155+
* Checks if a vertex has adjacent colored vertices
156+
*
157+
* @param graph the input graph
158+
* @param vertex the vertex to check
159+
* @param colors the array of colors assigned to the vertices
160+
* @return {@code true} if the vertex has adjacent colored vertices, {@code false} otherwise
161+
*/
90162
private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) {
91163
return graph.getAdjacencyList(vertex).stream().anyMatch(otherVertex -> !isBlank(colors[otherVertex]));
92164
}
93165

166+
/**
167+
* Initializes the colors array with blank color
168+
*
169+
* @param numberOfVertices the number of vertices in the graph
170+
* @return an array of integers representing the colors assigned to the vertices
171+
*/
94172
private static int[] initializeColors(int numberOfVertices) {
95173
int[] colors = new int[numberOfVertices];
96174
Arrays.fill(colors, BLANK_COLOR);
97175
return colors;
98176
}
99177

178+
/**
179+
* Sorts the vertices by their degree in descending order
180+
*
181+
* @param graph the input graph
182+
* @return an array of integers representing the vertices sorted by degree
183+
*/
100184
private static Integer[] getSortedNodes(final Graph graph) {
101185
return IntStream.range(0, graph.getNumVertices()).boxed().sorted(Comparator.comparingInt(v -> - graph.getAdjacencyList(v).size())).toArray(Integer[] ::new);
102186
}
103187

188+
/**
189+
* Computes the colors already used by the adjacent vertices
190+
*
191+
* @param graph the input graph
192+
* @param vertex the vertex to check
193+
* @param colors the array of colors assigned to the vertices
194+
* @return an array of booleans representing the colors used by the adjacent vertices
195+
*/
104196
private static boolean[] computeUsedColors(final Graph graph, final int vertex, final int[] colors) {
105197
boolean[] usedColors = new boolean[graph.getNumVertices()];
106198
graph.getAdjacencyList(vertex).stream().map(neighbor -> colors[neighbor]).filter(color -> !isBlank(color)).forEach(color -> usedColors[color] = true);
107199
return usedColors;
108200
}
109201

202+
/**
203+
* Finds the first unused color
204+
*
205+
* @param usedColors the array of colors used by the adjacent vertices
206+
* @return the first unused color
207+
*/
110208
private static int firstUnusedColor(boolean[] usedColors) {
111209
return IntStream.range(0, usedColors.length).filter(color -> !usedColors[color]).findFirst().getAsInt();
112210
}

src/test/java/com/thealgorithms/datastructures/graphs/WelshPowellTest.java

+36-26
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,25 @@ void testCompleteGraph() {
3434
assertEquals(3, countDistinctColors(colors));
3535
}
3636

37-
// The following test originates from the following website : https://www.geeksforgeeks.org/welsh-powell-graph-colouring-algorithm/
3837
@Test
3938
void testComplexGraph() {
4039
int[][] edges = {
41-
{0, 7}, // A-H
42-
{0, 1}, // A-B
43-
{1, 3}, // B-D
44-
{2, 3}, // C-D
45-
{3, 8}, // D-I
46-
{3, 10}, // D-K
47-
{4, 10}, // E-K
48-
{4, 5}, // E-F
49-
{5, 6}, // F-G
50-
{6, 10}, // G-K
51-
{6, 7}, // G-H
52-
{7, 8}, // H-I
53-
{7, 9}, // H-J
54-
{7, 10}, // H-K
55-
{8, 9}, // I-J
56-
{9, 10}, // J-K
40+
{0, 7},
41+
{0, 1},
42+
{1, 3},
43+
{2, 3},
44+
{3, 8},
45+
{3, 10},
46+
{4, 10},
47+
{4, 5},
48+
{5, 6},
49+
{6, 10},
50+
{6, 7},
51+
{7, 8},
52+
{7, 9},
53+
{7, 10},
54+
{8, 9},
55+
{9, 10},
5756
};
5857

5958
final var graph = WelshPowell.makeGraph(11, edges); // 11 vertices from A (0) to K (10)
@@ -86,24 +85,35 @@ void testInvalidEdgeArray() {
8685

8786
@Test
8887
void testWithPreColoredVertex() {
89-
// Create a linear graph with 4 vertices and edges connecting them in sequence
9088
final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}});
91-
92-
// Apply the Welsh-Powell coloring algorithm to the graph
9389
int[] colors = WelshPowell.findColoring(graph);
94-
95-
// Validate that the coloring is correct (no two adjacent vertices have the same color)
9690
assertTrue(isColoringValid(graph, colors));
97-
98-
// Check if the algorithm has used at least 2 colors (expected for a linear graph)
9991
assertTrue(countDistinctColors(colors) >= 2);
100-
101-
// Verify that all vertices have been assigned a color
10292
for (int color : colors) {
10393
assertTrue(color >= 0);
10494
}
10595
}
10696

97+
@Test
98+
void testLargeGraph() {
99+
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}};
100+
101+
final var graph = WelshPowell.makeGraph(16, edges); // 16 vertices
102+
int[] colors = WelshPowell.findColoring(graph);
103+
assertTrue(isColoringValid(graph, colors));
104+
assertEquals(3, countDistinctColors(colors)); // Expecting a maximum of 3 colors
105+
}
106+
107+
@Test
108+
void testStarGraph() {
109+
int[][] edges = {{0, 1}, {0, 2}, {0, 3}, {0, 4}};
110+
111+
final var graph = WelshPowell.makeGraph(5, edges); // 5 vertices in a star formation
112+
int[] colors = WelshPowell.findColoring(graph);
113+
assertTrue(isColoringValid(graph, colors));
114+
assertEquals(2, countDistinctColors(colors)); // Star graph can be colored with 2 colors
115+
}
116+
107117
private boolean isColoringValid(Graph graph, int[] colors) {
108118
if (Arrays.stream(colors).anyMatch(n -> n < 0)) {
109119
return false;

0 commit comments

Comments
 (0)