Clone Graph

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.


OJ's undirected graph serialization:

Nodes are labeled uniquely.

We use # as a separator for each node, and , as a separator for node label and each neighbor of the node.

As an example, consider the serialized graph {0,1,2#1,2#2,2}.

The graph has a total of three nodes, and therefore contains three parts as separated by #.

  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.

Visually, the graph looks like the following:

       1
      / \
     /   \
    0 --- 2
         / \
         \_/

My solution:

1. DFS

traverse all the node in the graph by using a hashset to record what nodes have been already dealt with(dealt with means the code has all its unprocessed neighbours.

/**
 * Definition for undirected graph.
 * class UndirectedGraphNode {
 *     int label;
 *     List<UndirectedGraphNode> neighbors;
 *     UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }
 * };
 */
public class Solution {
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        /*
        *  BFS
        *  use hashmap to record the mapping from the original graph to the cloned graph
        *  use a hashset to record what nodes have been dealt with(no more visits)
        */
        if(node == null)        return null;
        Map<UndirectedGraphNode,UndirectedGraphNode> map = new HashMap<UndirectedGraphNode,UndirectedGraphNode>();
        Set<UndirectedGraphNode> visited = new HashSet<UndirectedGraphNode>();
        
        Stack<UndirectedGraphNode> stack = new Stack<UndirectedGraphNode>();
        stack.push(node);
        
        while(!stack.empty()){
            UndirectedGraphNode top = stack.pop(); 
            if(!visited.contains(top)){ // top has not been copied
                visited.add(top);
                UndirectedGraphNode copy = map.get(top);
                if(copy == null){
                    copy = new UndirectedGraphNode(top.label);
                    map.put(top,copy);
                }
                for(UndirectedGraphNode neighbor:top.neighbors){
                    UndirectedGraphNode ncopy = map.get(neighbor);
                    if(ncopy == null){
                        ncopy = new UndirectedGraphNode(neighbor.label);
                        map.put(neighbor, ncopy);
                    }
                    copy.neighbors.add(ncopy);
                    if(!visited.contains(neighbor))      stack.push(neighbor);
                }
            }
            
            
        }
        
        return map.get(node);
    }
}

The time complexity is O(mn), where n is the # of the nodes in the graph and m is the # of (most) nodes in the neighbour list of each node.

The space complexity is O(n) from the hashmap and the visited hashset.


The same method by recursion:

public class Solution {
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        /*
        *  recursion?
        */
        if(node == null)        return null;
        Map<UndirectedGraphNode,UndirectedGraphNode> map = new HashMap<UndirectedGraphNode,UndirectedGraphNode>();
        Set<UndirectedGraphNode> visited = new HashSet<UndirectedGraphNode>();
        cloneRec(node, map, visited);
        return map.get(node);
    }
    
    private void cloneRec(UndirectedGraphNode node, Map<UndirectedGraphNode,UndirectedGraphNode> map, Set<UndirectedGraphNode> visited){
        if(visited.contains(node))      return;
        UndirectedGraphNode copy = map.get(node);
        if(copy == null){
            copy = new UndirectedGraphNode(node.label);
            map.put(node,copy);
        }
        
        for(UndirectedGraphNode neighbor: node.neighbors){
            if(map.get(neighbor) == null)
                cloneRec(neighbor, map, visited);
            node.neighbors.add(map.get(neighbor));
        }
        visited.add(node);
    }
    
}

This throws java.util.ConcurrentModificationException. I guess some data structures are not allowed to access simultaneously.

The correct recursion:

public class Solution {
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        /*
        *  recursion?
        */
        if(node == null)        return null;
        Map<UndirectedGraphNode,UndirectedGraphNode> map = new HashMap<UndirectedGraphNode,UndirectedGraphNode>();
        return cloneRec(node, map);
    }
    
    private UndirectedGraphNode cloneRec(UndirectedGraphNode node, Map<UndirectedGraphNode,UndirectedGraphNode> map){
        // note that node is not null
        UndirectedGraphNode found = map.get(node);
        if(found == null){
            // not cloned yet
            found = new UndirectedGraphNode(node.label);
            map.put(node,found);
            for(UndirectedGraphNode neighbor: node.neighbors){
                UndirectedGraphNode ncopy = cloneRec(neighbor,map);
                found.neighbors.add(ncopy);
            }
        }
        
        return found;
    }
    
}

The key is: processing a node only when all its neighbours are ready in the map.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章