forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWelshPowell.java
More file actions
212 lines (194 loc) · 7.82 KB
/
WelshPowell.java
File metadata and controls
212 lines (194 loc) · 7.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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 that aims to color a graph
* using the minimum number of colors such that no two adjacent vertices share the same color.
*
* <p>
* The algorithm works by:
* <ol>
* <li>Sorting the vertices in descending order based on their degrees (number of edges connected).</li>
* <li>Iterating through each vertex and assigning it the smallest available color that has not been used by its adjacent vertices.</li>
* <li>Coloring adjacent vertices with the same color is avoided.</li>
* </ol>
* </p>
*
* <p>
* For more information, see <a href="https://en.wikipedia.org/wiki/Graph_coloring">Graph Coloring</a>.
* </p>
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public final class WelshPowell {
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 final HashSet<Integer>[] 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");
}
adjacencyLists = new HashSet[vertices];
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);
if (nodeA == nodeB) {
throw new IllegalArgumentException("Self-loops are not allowed");
}
adjacencyLists[nodeA].add(nodeB);
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<Integer> 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) {
if (edge.length != 2) {
throw new IllegalArgumentException("Edge array must have exactly two elements");
}
graph.addEdge(edge[0], edge[1]);
}
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);
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;
}
/**
* 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();
}
}