Skip to content

Commit 47a9b1b

Browse files
straf10vil02
andauthored
Add WelshPowell (Graph Colouring) (TheAlgorithms#5034)
* Welsh Powell Algorithm + Test --------- Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com>
1 parent 55cc562 commit 47a9b1b

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.HashSet;
6+
import java.util.stream.IntStream;
7+
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
12+
*/
13+
14+
public final class WelshPowell {
15+
private static final int BLANK_COLOR = -1; // Representing uncolored state
16+
17+
private WelshPowell() {
18+
}
19+
20+
static class Graph {
21+
private HashSet<Integer>[] adjacencyLists;
22+
23+
private Graph(int vertices) {
24+
if (vertices < 0) {
25+
throw new IllegalArgumentException("Number of vertices cannot be negative");
26+
}
27+
28+
adjacencyLists = new HashSet[vertices];
29+
Arrays.setAll(adjacencyLists, i -> new HashSet<>());
30+
}
31+
32+
private void addEdge(int nodeA, int nodeB) {
33+
validateVertex(nodeA);
34+
validateVertex(nodeB);
35+
if (nodeA == nodeB) {
36+
throw new IllegalArgumentException("Self-loops are not allowed");
37+
}
38+
adjacencyLists[nodeA].add(nodeB);
39+
adjacencyLists[nodeB].add(nodeA);
40+
}
41+
42+
private void validateVertex(int vertex) {
43+
if (vertex < 0 || vertex >= getNumVertices()) {
44+
throw new IllegalArgumentException("Vertex " + vertex + " is out of bounds");
45+
}
46+
}
47+
48+
HashSet<Integer> getAdjacencyList(int vertex) {
49+
return adjacencyLists[vertex];
50+
}
51+
52+
int getNumVertices() {
53+
return adjacencyLists.length;
54+
}
55+
}
56+
57+
public static Graph makeGraph(int numberOfVertices, int[][] listOfEdges) {
58+
Graph graph = new Graph(numberOfVertices);
59+
for (int[] edge : listOfEdges) {
60+
if (edge.length != 2) {
61+
throw new IllegalArgumentException("Edge array must have exactly two elements");
62+
}
63+
graph.addEdge(edge[0], edge[1]);
64+
}
65+
return graph;
66+
}
67+
68+
public static int[] findColoring(Graph graph) {
69+
int[] colors = initializeColors(graph.getNumVertices());
70+
Integer[] sortedVertices = getSortedNodes(graph);
71+
for (int vertex : sortedVertices) {
72+
if (isBlank(colors[vertex])) {
73+
boolean[] usedColors = computeUsedColors(graph, vertex, colors);
74+
final var newColor = firstUnusedColor(usedColors);
75+
colors[vertex] = newColor;
76+
Arrays.stream(sortedVertices).forEach(otherVertex -> {
77+
if (isBlank(colors[otherVertex]) && !isAdjacentToColored(graph, otherVertex, colors)) {
78+
colors[otherVertex] = newColor;
79+
}
80+
});
81+
}
82+
}
83+
return colors;
84+
}
85+
86+
private static boolean isBlank(int color) {
87+
return color == BLANK_COLOR;
88+
}
89+
90+
private static boolean isAdjacentToColored(Graph graph, int vertex, int[] colors) {
91+
return graph.getAdjacencyList(vertex).stream().anyMatch(otherVertex -> !isBlank(colors[otherVertex]));
92+
}
93+
94+
private static int[] initializeColors(int numberOfVertices) {
95+
int[] colors = new int[numberOfVertices];
96+
Arrays.fill(colors, BLANK_COLOR);
97+
return colors;
98+
}
99+
100+
private static Integer[] getSortedNodes(final Graph graph) {
101+
return IntStream.range(0, graph.getNumVertices()).boxed().sorted(Comparator.comparingInt(v -> - graph.getAdjacencyList(v).size())).toArray(Integer[] ::new);
102+
}
103+
104+
private static boolean[] computeUsedColors(final Graph graph, final int vertex, final int[] colors) {
105+
boolean[] usedColors = new boolean[graph.getNumVertices()];
106+
graph.getAdjacencyList(vertex).stream().map(neighbor -> colors[neighbor]).filter(color -> !isBlank(color)).forEach(color -> usedColors[color] = true);
107+
return usedColors;
108+
}
109+
110+
private static int firstUnusedColor(boolean[] usedColors) {
111+
return IntStream.range(0, usedColors.length).filter(color -> !usedColors[color]).findFirst().getAsInt();
112+
}
113+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.thealgorithms.datastructures.graphs;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import com.thealgorithms.datastructures.graphs.WelshPowell.Graph;
8+
import java.util.Arrays;
9+
import org.junit.jupiter.api.Test;
10+
11+
class WelshPowellTest {
12+
13+
@Test
14+
void testSimpleGraph() {
15+
final var graph = WelshPowell.makeGraph(4, new int[][] {{0, 1}, {1, 2}, {2, 3}});
16+
int[] colors = WelshPowell.findColoring(graph);
17+
assertTrue(isColoringValid(graph, colors));
18+
assertEquals(2, countDistinctColors(colors));
19+
}
20+
21+
@Test
22+
void testDisconnectedGraph() {
23+
final var graph = WelshPowell.makeGraph(3, new int[][] {}); // No edges
24+
int[] colors = WelshPowell.findColoring(graph);
25+
assertTrue(isColoringValid(graph, colors));
26+
assertEquals(1, countDistinctColors(colors));
27+
}
28+
29+
@Test
30+
void testCompleteGraph() {
31+
final var graph = WelshPowell.makeGraph(3, new int[][] {{0, 1}, {1, 2}, {2, 0}});
32+
int[] colors = WelshPowell.findColoring(graph);
33+
assertTrue(isColoringValid(graph, colors));
34+
assertEquals(3, countDistinctColors(colors));
35+
}
36+
37+
// The following test originates from the following website : https://www.geeksforgeeks.org/welsh-powell-graph-colouring-algorithm/
38+
@Test
39+
void testComplexGraph() {
40+
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
57+
};
58+
59+
final var graph = WelshPowell.makeGraph(11, edges); // 11 vertices from A (0) to K (10)
60+
int[] colors = WelshPowell.findColoring(graph);
61+
62+
assertTrue(isColoringValid(graph, colors), "The coloring should be valid with no adjacent vertices sharing the same color.");
63+
assertEquals(3, countDistinctColors(colors), "The chromatic number of the graph should be 3.");
64+
}
65+
66+
@Test
67+
void testNegativeVertices() {
68+
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(-1, new int[][] {}); }, "Number of vertices cannot be negative");
69+
}
70+
71+
@Test
72+
void testSelfLoop() {
73+
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 0}}); }, "Self-loops are not allowed");
74+
}
75+
76+
@Test
77+
void testInvalidVertex() {
78+
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, 3}}); }, "Vertex out of bounds");
79+
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0, -1}}); }, "Vertex out of bounds");
80+
}
81+
82+
@Test
83+
void testInvalidEdgeArray() {
84+
assertThrows(IllegalArgumentException.class, () -> { WelshPowell.makeGraph(3, new int[][] {{0}}); }, "Edge array must have exactly two elements");
85+
}
86+
87+
@Test
88+
void testWithPreColoredVertex() {
89+
// Create a linear graph with 4 vertices and edges connecting them in sequence
90+
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
93+
int[] colors = WelshPowell.findColoring(graph);
94+
95+
// Validate that the coloring is correct (no two adjacent vertices have the same color)
96+
assertTrue(isColoringValid(graph, colors));
97+
98+
// Check if the algorithm has used at least 2 colors (expected for a linear graph)
99+
assertTrue(countDistinctColors(colors) >= 2);
100+
101+
// Verify that all vertices have been assigned a color
102+
for (int color : colors) {
103+
assertTrue(color >= 0);
104+
}
105+
}
106+
107+
private boolean isColoringValid(Graph graph, int[] colors) {
108+
if (Arrays.stream(colors).anyMatch(n -> n < 0)) {
109+
return false;
110+
}
111+
for (int i = 0; i < graph.getNumVertices(); i++) {
112+
for (int neighbor : graph.getAdjacencyList(i)) {
113+
if (i != neighbor && colors[i] == colors[neighbor]) {
114+
return false; // Adjacent vertices have the same color
115+
}
116+
}
117+
}
118+
return true; // No adjacent vertices share the same color
119+
}
120+
121+
private int countDistinctColors(int[] colors) {
122+
return (int) Arrays.stream(colors).distinct().count();
123+
}
124+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy