Given an undirected graph and a number m, determine if the graph can be colored with at most m colors such that no two adjacent vertices of the graph are colored with same color. Here coloring of a graph means assignment of colors to all vertices. >Special request to verify complexity: > Complexity: > Time: > O(E * V*V) :where E is edges and colorNum, and V is number of vertices. > Note: > Although it appears that > O(E * M*V) : where, M is number of colors, but truth is the for loop for colors, would never count more than number of vertices. > Check 4th node. even if numColors "M" were 25, it would still loop only max "V" times. > Thus complexity is V*V and not V*M. > 1 > | \ > 2----4 > | / > 3 > Space: > O(V) - the node-color map used to store precomputed colors. Looking for code-review, optimizations and best practices. class GraphColor<T> implements Iterable<T> { /* A map from nodes in the graph to sets of outgoing edges. Each * set of edges is represented by a map from edges to doubles. */ private final Map<T, Set<T>> graph = new HashMap<T, Set<T>>(); /** * Adds a new node to the graph. If the node already exists then its a * no-op. * * @param node Adds to a graph. If node is null then this is a no-op. * @return true if node is added, false otherwise. */ public boolean addNode(T node) { if (node == null) { throw new NullPointerException("The input node cannot be null."); } if (graph.containsKey(node)) return false; graph.put(node, new HashSet<T>()); return true; } /** * Given the source and destination node it would add an arc from source * to destination node. If an arc already exists then the value would be * updated the new value. * * @param nodeA the source node. * @param nodeB the destination node. * @throws NullPointerException if source or destination is null. * @throws NoSuchElementException if either source of destination does not exists. */ public void addEdge (T nodeA, T nodeB) { if (nodeA == null || nodeB == null) { throw new NullPointerException("Source and Destination, both should be non-null."); } if (!graph.containsKey(nodeA) || !graph.containsKey(nodeB)) { throw new NoSuchElementException("Source and Destination, both should be part of graph"); } /* A node would always be added so no point returning true or false */ graph.get(nodeA).add(nodeB); graph.get(nodeB).add(nodeA); } /** * Removes an edge from the graph. * * @param nodeA If the source node. * @param nodeB If the destination node. * @throws NullPointerException if either source or destination specified is null * @throws NoSuchElementException if graph does not contain either source or destination */ public void removeEdge (T nodeA, T nodeB) { if (nodeA == null || nodeB == null) { throw new NullPointerException("Source and Destination, both should be non-null."); } if (!graph.containsKey(nodeA) || !graph.containsKey(nodeB)) { throw new NoSuchElementException("Source and Destination, both should be part of graph"); } graph.get(nodeA).remove(nodeB); graph.get(nodeB).remove(nodeA); } /** * Given a node, returns the edges going outward that node, * as an immutable map. * * @param node The node whose edges should be queried. * @return An immutable view of the edges leaving that node. * @throws NullPointerException If input node is null. * @throws NoSuchElementException If node is not in graph. */ public Set<T> edgesFrom(T node) { if (node == null) { throw new NullPointerException("The node should not be null."); } Set<T> edges = graph.get(node); if (edges == null) { throw new NoSuchElementException("Source node does not exist."); } return Collections.unmodifiableSet(edges); } /** * Returns the iterator that travels the nodes of a graph. * * @return an iterator that travels the nodes of a graph. */ @Override public Iterator<T> iterator() { return graph.keySet().iterator(); } } public final class ColorGraph<T> { private ColorGraph() {} /** * Given a graph and number of colors, colors the graph such that no adjacent nodes have same color. * Note: the map of nodes and colors is one-of the possible solutions that may eists. * * @param graph the graph to be colored * @param colorCount the number of colors * @return the map of nodes to the colors. */ public static <T> Map<T, Integer> getNodeColor (GraphColor<T> graph, int colorCount) { final Map<T, Integer> nodeColor = new HashMap<T, Integer>(); if (compute(graph, graph.iterator().next(), null, colorCount, 0, new HashSet<T>(), nodeColor)) { return nodeColor; } else { throw new IllegalArgumentException("Number of colors inadequet to color graph."); } } private static <T> boolean compute (GraphColor<T> graph, T node, T parent, int colorCount, int parentColor, Set<T> visited, Map<T, Integer> nodeColor) { if (visited.contains(node)) return true; visited.add(node); // check all colors and pick the color which is valid. int color = 0; boolean validColor = false; for (color = 0; color < colorCount; color++) { validColor = true; for (T neighborNode : graph.edgesFrom(node)) { if (nodeColor.containsKey(neighborNode) && nodeColor.get(neighborNode) == color) { validColor = false; } } if (validColor) { break; } } if (!validColor) return false; nodeColor.put(node, color); boolean finalResult = true; for (T node1 : graph.edgesFrom(node)) { finalResult = finalResult && compute(graph, node1, node, colorCount, parentColor, visited, nodeColor); } return finalResult; } } public class ColorGraphTest { @Test public void test1() { GraphColor<Integer> gcd1 = new GraphColor<Integer>(); gcd1.addNode(1); gcd1.addNode(2); gcd1.addNode(3); gcd1.addEdge(1, 2); gcd1.addEdge(2, 3); gcd1.addEdge(1, 3); Map<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(1, 0); map.put(2, 1); map.put(3, 2); assertEquals(map, ColorGraph.getNodeColor(gcd1, 20)); } @Test public void test2() { GraphColor<Integer> gcd2 = new GraphColor<Integer>(); gcd2.addNode(1); gcd2.addNode(2); gcd2.addNode(3); gcd2.addNode(4); gcd2.addNode(5); gcd2.addEdge(1, 2); gcd2.addEdge(2, 3); gcd2.addEdge(2, 4); gcd2.addEdge(3, 4); gcd2.addEdge(4, 5); Map<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(1, 0); map.put(2, 1); map.put(3, 0); map.put(4, 2); map.put(5, 0); assertEquals(map, ColorGraph.getNodeColor(gcd2, 20)); } @Test public void test3() { GraphColor<Integer> gcd3 = new GraphColor<Integer>(); gcd3.addNode(1); gcd3.addNode(2); gcd3.addNode(3); gcd3.addNode(4); gcd3.addNode(5); gcd3.addEdge(1, 2); gcd3.addEdge(2, 3); gcd3.addEdge(2, 4); gcd3.addEdge(3, 4); gcd3.addEdge(4, 5); gcd3.addEdge(5, 2); Map<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(1, 0); map.put(2, 1); map.put(3, 0); map.put(4, 2); map.put(5, 0); assertEquals(map, ColorGraph.getNodeColor(gcd3, 3)); } @Test public void test4() { GraphColor<Integer> gcd4 = new GraphColor<Integer>(); gcd4.addNode(1); gcd4.addNode(2); gcd4.addNode(3); gcd4.addNode(4); gcd4.addEdge(1, 2); gcd4.addEdge(2, 3); gcd4.addEdge(2, 4); Map<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(1, 0); map.put(2, 1); map.put(3, 0); map.put(4, 0); assertEquals(map, ColorGraph.getNodeColor(gcd4, 3)); } }