Skip to content

Commit 176257e

Browse files
committed
Add graph_coloring_dsatur.cpp to graph directory
1 parent e62e94d commit 176257e

File tree

1 file changed

+204
-0
lines changed

1 file changed

+204
-0
lines changed

graph/graph_coloring_dsatur.cpp

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#include <iostream>
2+
#include <set>
3+
#include <vector>
4+
#include <algorithm>
5+
#include <cassert>
6+
7+
/**
8+
* @brief [DSatur graph coloring algorithm]
9+
*
10+
* @author [Magdy Sedra](https://github.com/MSedra)
11+
*
12+
* @details
13+
* Graph Coloring: It is a way to color nodes or edges of a graph so that no adjacent nodes or edges have the same color.
14+
* Below is an implementation for the DSatur algorithm, which is a heuristic algorithm that finds the chromatic number (the minimum number of colors
15+
* needed to color the graph) and a valid coloring. It is implemented using red black tree (std::set) for efficiency.
16+
* Nodes are traversed in order of:
17+
* 1) Highest Saturation Degree (number of unique colors in neighborhood).
18+
* 2) In case of ties, Highest Degree in the uncolored subgraph.
19+
* 3) In case of ties, Lower node index
20+
* Saturation of a node: It is the number of distinct colors that its neighbors are colored with.
21+
* A node picked is colored with the minimum possible color [1, maxDegree+1] and then is removed
22+
* from the graph by removing its edge from the degrees of its neighbors
23+
*/
24+
25+
26+
/**
27+
* @brief [Graph class]
28+
*
29+
* @details
30+
* A class representing a graph.
31+
*/
32+
33+
namespace graphcoloring {
34+
35+
class Graph {
36+
std::vector<std::vector<int>> adjList;
37+
std::vector<int> degrees;
38+
public:
39+
40+
Graph(int nodesCount) {
41+
assert(nodesCount > 0);
42+
adjList.resize(nodesCount);
43+
degrees.resize(nodesCount);
44+
}
45+
46+
/**
47+
* @brief Function returns the number of nodes in the graph
48+
*
49+
* @return the size of adjacency list which is equivalent to the number of nodes
50+
*/
51+
int getNodesCount() const {
52+
return adjList.size();
53+
}
54+
55+
/**
56+
* @brief Function adds an edge in the graph
57+
*
58+
* @param nodeA -> the first node in the edge
59+
* @param nodeB -> the second node in the edge
60+
*/
61+
void addEdge(int nodeA, int nodeB) {
62+
int nodesCount = getNodesCount();
63+
assert(nodeA >= 0 && nodeA < nodesCount);
64+
assert(nodeB >= 0 && nodeB < nodesCount);
65+
adjList[nodeA].push_back(nodeB);
66+
adjList[nodeB].push_back(nodeA);
67+
++degrees[nodeA];
68+
++degrees[nodeB];
69+
}
70+
71+
/**
72+
* @brief Function returns the neighbors of a node
73+
*
74+
* @param node -> the node whose neighbors are requested
75+
*
76+
* @return the neighbors vector of the parameter node
77+
*/
78+
const std::vector<int>& getNeighbors(int node) const {
79+
int nodesCount = getNodesCount();
80+
assert(node >= 0 && node < nodesCount);
81+
return adjList[node];
82+
}
83+
84+
/**
85+
* @brief Function returns the degrees vector of the graph such that
86+
* degrees[i] is the count of neighbors of node i.
87+
*
88+
* @return the degrees vector
89+
*/
90+
std::vector<int> getDegrees() const {
91+
return degrees;
92+
}
93+
};
94+
95+
class DSatur {
96+
std::vector<int> colors;
97+
public:
98+
/**
99+
* @brief Function runs the DSatur algorithm for a graph by find an optimal vertices coloring
100+
* such that no 2 vertices sharing an edge have the same color. The function sets the resulting colors in the colors vector.
101+
*
102+
* @param graph -> input graph
103+
*/
104+
void solve(const Graph& graph) {
105+
int nodesCount = graph.getNodesCount();
106+
std::vector<int> degrees = graph.getDegrees();
107+
int maxDegree = *max_element(degrees.begin(), degrees.end());
108+
std::vector<std::vector<bool>> neighboringColors(nodesCount, std::vector<bool>(maxDegree + 2));
109+
std::vector<int> saturation(nodesCount);
110+
111+
auto cmp = [&](int nodeA, int nodeB) {
112+
if (saturation[nodeA] != saturation[nodeB])
113+
return saturation[nodeA] > saturation[nodeB];
114+
if (degrees[nodeA] != degrees[nodeB])
115+
return degrees[nodeA] > degrees[nodeB];
116+
return nodeA < nodeB;
117+
};
118+
std::set<int, decltype(cmp)> tree(cmp);
119+
120+
colors.clear();
121+
colors.resize(nodesCount, 0);
122+
for (int i = 0; i < nodesCount; ++i)
123+
tree.insert(i);
124+
125+
while (!tree.empty()) {
126+
int nodeToColor = *tree.begin();
127+
int color = getValidColor(neighboringColors[nodeToColor], maxDegree);
128+
colors[nodeToColor] = color;
129+
for (auto neighbor : graph.getNeighbors(nodeToColor))
130+
if (!colors[neighbor]) {
131+
tree.erase(neighbor);
132+
if (!neighboringColors[neighbor][color]) {
133+
++saturation[neighbor];
134+
neighboringColors[neighbor][color] = true;
135+
}
136+
--degrees[neighbor];
137+
tree.insert(neighbor);
138+
}
139+
tree.erase(nodeToColor);
140+
}
141+
142+
}
143+
144+
/**
145+
* @brief Function gets the minimum color [1, maxDegree+1] such that the number of neighbors
146+
* colored with this color = 0
147+
*
148+
* @param neighborsColors -> neighborsColors[i] is true if there is at least 1 neighbor colored with color i
149+
* @param maxDegree -> maximum degree of a node in the graph
150+
*
151+
* @return the minimum valid color [1, maxDegree+1]
152+
*/
153+
int getValidColor(const std::vector<bool>& neighborsColors, const int& maxDegree) const {
154+
for (int i = 1; i <= maxDegree + 1; ++i)
155+
if (!neighborsColors[i])
156+
return i;
157+
}
158+
159+
/**
160+
* @brief Function returns colors vector resulting from the DSatur algorithm
161+
*
162+
* @return the colors vector
163+
*/
164+
std::vector<int> getColors() const {
165+
return colors;
166+
}
167+
168+
/**
169+
* @brief Function returns the Chromatic Number resulting from the DSatur algorithm
170+
*
171+
* @return the maximum color in the colors vector, which is the Chromatic Number
172+
*/
173+
int getChromaticNumber() const {
174+
return *max_element(colors.begin(), colors.end());
175+
}
176+
};
177+
}
178+
179+
static void test() {
180+
181+
const int V = 4;
182+
183+
graphcoloring::Graph graph(V);
184+
graph.addEdge(0, 1);
185+
graph.addEdge(0, 2);
186+
graph.addEdge(0, 3);
187+
graph.addEdge(1, 2);
188+
graph.addEdge(2, 3);
189+
190+
graphcoloring::DSatur dSatur;
191+
dSatur.solve(graph);
192+
193+
std::cout << "Coloring graph with " << dSatur.getChromaticNumber() << " colors." << std::endl;
194+
const std::vector<int> colors = dSatur.getColors();
195+
for (int i = 0; i < V; ++i)
196+
std::cout << "Node " << i << " is colored with " << colors[i] << std::endl;
197+
198+
}
199+
200+
int main() {
201+
202+
test();
203+
return 0;
204+
}

0 commit comments

Comments
 (0)