Minimum Number of Colours Required to Colour a Graph


The Minimum Number of Colours Required to Colour a Graph is a fundamental graph theory issue that includes colouring vertices so that no two neighbouring vertices have the same colour. Determine the least amount of colours needed for a valid colouring.Greedy Colouring is a simple and commonly used technique that colours vertices one by one based on their neighbours. Backtracking also carefully analyses all colour allocations. DSatur−based graph colouring prioritises vertices with the highest degree and saturation.

Methods Used

  • Greedy colouring

  • Backtracking

  • Graph colouring

Greedy Colouring Method

The Greedy Colouring technique makes graph colouring easy. It colours the first vertex and iterates over the others. It assigns the least colour to each vertex by checking its neighbouring vertices. This "greedy" technique creates locally optimum judgements without considering colour consumption. Greedy Colouring is straightforward to apply and usually works well, although it may not provide the least amount of colours.

Algorithm

  • Initialise all members of an array 'colours' of size 'numVertices' to −1.

  • Colour the first graph vertex 0.

  • For each vertex 'v' from 1 to numVertices−1, execute the following:

    • Store neighbouring vertex colours in an empty set 'usedColors'.

      Iterate through 'v's nearby vertices:

    • Add the neighbouring vertex's non−zero colour to 'usedColors'.

    • Start at 0 and increase until 'usedColors' doesn't include the smallest colour.

    • Give 'v' in the 'colours' array the smallest colour.

  • Find the maximum colour utilised in the 'colours' array and return +1 as'minColors'.

Example

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>

// Structure to represent an edge in the graph
struct Edge {
    int src, dest;
};

// Function to add an edge to the graph
void addEdge(std::vector<std::vector<int>>& graph, int src, int dest) {
    graph[src].push_back(dest);
    graph[dest].push_back(src);
}

// Greedy Coloring Algorithm
int greedyColoring(const std::vector<std::vector<int>>& graph, int numVertices) {
    std::vector<int> colors(numVertices, -1);
    colors[0] = 0;

    for (int vertex = 1; vertex < numVertices; ++vertex) {
        std::unordered_set<int> usedColors;
        for (int neighbor : graph[vertex]) {
            if (colors[neighbor] != -1) {
                usedColors.insert(colors[neighbor]);
            }
        }

        int availableColor = 0;
        while (usedColors.count(availableColor) != 0) {
            ++availableColor;
        }

        colors[vertex] = availableColor;
    }

    return *std::max_element(colors.begin(), colors.end()) + 1;
}

int main() {
    // Example usage
    int numVertices = 6;
    std::vector<Edge> edges = {
        {0, 1},
        {0, 2},
        {1, 2},
        {1, 3},
        {2, 3},
        {3, 4},
        {4, 5}
    };
    int numEdges = edges.size();

    std::vector<std::vector<int>> graph(numVertices);
    for (const auto& edge : edges) {
        addEdge(graph, edge.src, edge.dest);
    }

    int minColorsGreedy = greedyColoring(graph, numVertices);
   
    std::cout << "Minimum number of colors (Greedy Coloring): " << minColorsGreedy << std::endl;
   
    return 0;
}

Output

Minimum number of colors (Greedy Coloring): 3

Backtracking Method

Backtracking is a method for finding all feasible answers. Backtracking recursively assigns colours to vertices in graph colouring. When a problem emerges, it undoes the colour assignment and considers alternatives. Backtracking ensures the minimal amount of colours needed to colour the graph, although it is computationally costly for big graphs owing to its thorough search.

Algorithm

  • Initialise all members of an array 'colours' of size 'numVertices' to −1.

  • For 'numColors' from 1 to numVertices:

  • Return 'numColors' as'minColors' if graphColoringUtil(graph, numColors, colours, 0) returns true.

  • Return 'numVertices' as'minColors' if no acceptable colouring is found.

Example

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>

// Structure to represent an edge in the graph
struct Edge {
    int src, dest;
};

// Function to add an edge to the graph
void addEdge(std::vector<std::vector<int>>& graph, int src, int dest) {
    graph[src].push_back(dest);
    graph[dest].push_back(src);
}


// Backtracking Algorithm
bool isSafe(int vertex, const std::vector<std::vector<int>>& graph, const std::vector<int>& colors, int color) {
    for (int neighbor : graph[vertex]) {
        if (colors[neighbor] == color) {
            return false;
        }
    }
    return true;
}

bool graphColoringUtil(const std::vector<std::vector<int>>& graph, int numColors, std::vector<int>& colors, int vertex) {
    if (vertex == colors.size()) {
        return true;
    }

    for (int color = 0; color < numColors; ++color) {
        if (isSafe(vertex, graph, colors, color)) {
            colors[vertex] = color;
            if (graphColoringUtil(graph, numColors, colors, vertex + 1)) {
                return true;
            }
            colors[vertex] = -1;
        }
    }

    return false;
}

int backtrackingColoring(const std::vector<std::vector<int>>& graph, int numVertices) {
    std::vector<int> colors(numVertices, -1);

    for (int numColors = 1; numColors <= numVertices; ++numColors) {
        if (graphColoringUtil(graph, numColors, colors, 0)) {
            return numColors;
        }
    }

    return numVertices;
}

int main() {
    // Example usage
    int numVertices = 6;
    std::vector<Edge> edges = {
        {0, 1},
        {0, 2},
        {1, 2},
        {1, 3},
        {2, 3},
        {3, 4},
        {4, 5}
    };
    int numEdges = edges.size();

    std::vector<std::vector<int>> graph(numVertices);
    for (const auto& edge : edges) {
        addEdge(graph, edge.src, edge.dest);
    }

    int minColorsBacktracking = backtrackingColoring(graph, numVertices);
    std::cout << "Minimum number of colors (Backtracking): " << minColorsBacktracking << std::endl;

    return 0;
}


Output

Minimum number of colors (Backtracking): 3

Graph Colouring Method

The sophisticated graph colouring heuristic DSatur (Maximum Degree of Saturation) reduces colour use. The highest−degree vertex is coloured first. The degree of saturation, or the amount of hues used by each vertex's neighbours, is next examined. In order to break ties, DSatur paints the vertex with the highest saturation level.

This approach optimises colour combinations based on vertex degrees and saturation degrees.

Algorithm

  • Initialise an array 'colours' of size 'numVertices' to −1.

  • Initialise a'saturation' array of size 'numVertices' to 0.

  • Make'saturationMap' empty.

  • Colour the graph's highest−degree vertex 0.

  • Update saturation and saturationMap for nearby vertices of the chosen vertex.

Example

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>

// Structure to represent an edge in the graph
struct Edge {
    int src, dest;
};

// Function to add an edge to the graph
void addEdge(std::vector<std::vector<int>>& graph, int src, int dest) {
    graph[src].push_back(dest);
    graph[dest].push_back(src);
}

// Graph Coloring using DSatur Algorithm
int getMaxDegreeVertex(const std::vector<std::vector<int>>& graph, const std::vector<int>& saturation) {
    int maxDegree = -1;
    int maxDegreeVertex = -1;

    for (int vertex = 0; vertex < graph.size(); ++vertex) {
        if (saturation[vertex] == -1 && graph[vertex].size() > maxDegree) {
            maxDegree = graph[vertex].size();
            maxDegreeVertex = vertex;
        }
    }

    return maxDegreeVertex;
}

int dsaturColoring(const std::vector<std::vector<int>>& graph, int numVertices) {
    std::vector<int> colors(numVertices, -1);
    std::vector<int> saturation(numVertices, -1);
    std::unordered_map<int, int> saturationMap;

    colors[0] = 0;

    for (int vertex = 1; vertex < numVertices; ++vertex) {
        for (int neighbor : graph[vertex]) {
            if (colors[neighbor] != -1) {
                saturation[vertex]++;
                saturationMap[colors[neighbor]]++;
            }
        }
    }

    while (*std::max_element(colors.begin(), colors.end()) == -1) {
        int maxDegreeVertex = getMaxDegreeVertex(graph, saturation);

        std::unordered_set<int> usedColors;
        for (int neighbor : graph[maxDegreeVertex]) {
            if (colors[neighbor] != -1) {
                usedColors.insert(colors[neighbor]);
            }
        }

        int availableColor = 0;
        while (usedColors.count(availableColor) != 0) {
            ++availableColor;
        }

        colors[maxDegreeVertex] = availableColor;

        for (int neighbor : graph[maxDegreeVertex]) {
            if (colors[neighbor] == -1) {
                saturation[neighbor]++;
            }
        }

        saturationMap.clear();
        for (int vertex = 0; vertex < numVertices; ++vertex) {
            if (colors[vertex] == -1) {
                saturationMap[saturation[vertex]]++;
            }
        }

        int maxSaturation = -1;
        for (auto& entry : saturationMap) {
            if (entry.second > maxSaturation) {
                maxSaturation = entry.second;
            }
        }

        std::vector<int> maxSaturationVertices;
        for (int vertex = 0; vertex < numVertices; ++vertex) {
            if (colors[vertex] == -1 && saturation[vertex] == maxSaturation) {
                maxSaturationVertices.push_back(vertex);
            }
        }

        if (maxSaturationVertices.size() > 1) {
            int maxDegree = -1;
            for (int vertex : maxSaturationVertices) {
                if (graph[vertex].size() > maxDegree) {
                    maxDegree = graph[vertex].size();
                    maxDegreeVertex = vertex;
                }
            }
        }
        else {
            maxDegreeVertex = maxSaturationVertices[0];
        }
    }

    return *std::max_element(colors.begin(), colors.end()) + 1;
}

int main() {
    // Example usage
    int numVertices = 6;
    std::vector<Edge> edges = {
        {0, 1},
        {0, 2},
        {1, 2},
        {1, 3},
        {2, 3},
        {3, 4},
        {4, 5}
    };
    int numEdges = edges.size();

    std::vector<std::vector<int>> graph(numVertices);
    for (const auto& edge : edges) {
        addEdge(graph, edge.src, edge.dest);
    }


    int minColorsDSatur = dsaturColoring(graph, numVertices);

    std::cout << "Minimum number of colors (DSatur): " << minColorsDSatur << std::endl;

    return 0;
}

Output

Minimum number of colors (DSatur): 1

Conclusion

Conclusion, graph theory's minimal colour issue has many applications. We discussed Greedy Colouring, Backtracking, and DSatur Graph Colouring to address this problem.Greedy Colouring is easy, but it may not always use the fewest colours. Backtracking ensures the optimum answer but is computationally costly for bigger networks. DSatur's sophisticated heuristic considers vertex degrees and saturation degrees to optimise graph colouring.

Updated on: 17-Jul-2023

280 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements