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