LeetCode 133 - Clone Graph Java - LintCode 137


https://leetcode.com/problems/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
         / \
         \_/
 * Definition for undirected graph.
 * class UndirectedGraphNode {
 *     int label;
 *     List<UndirectedGraphNode> neighbors;
 *     UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }

X. DFS
https://discuss.leetcode.com/topic/26560/java-solution-with-dfs-and-bfs
private Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
// DFS
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
    if (node == null) return null;
    if (map.containsKey(node)) return map.get(node);
    UndirectedGraphNode copy = new UndirectedGraphNode(node.label);
    map.put(node, copy);
    for (UndirectedGraphNode n : node.neighbors)
        copy.neighbors.add(cloneGraph(n));
    return copy;
}
https://discuss.leetcode.com/topic/9629/depth-first-simple-java-solution
    private HashMap<Integer, UndirectedGraphNode> map = new HashMap<>();
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        return clone(node);
    }

    private UndirectedGraphNode clone(UndirectedGraphNode node) {
        if (node == null) return null;
        
        if (map.containsKey(node.label)) {
            return map.get(node.label);
        }
        UndirectedGraphNode clone = new UndirectedGraphNode(node.label);
        map.put(clone.label, clone);
        for (UndirectedGraphNode neighbor : node.neighbors) {
            clone.neighbors.add(clone(neighbor));
        }
        return clone;
    }
can this approach handle the case that there are nodes having the same label value in a cycle?. I think it might be better to use the HashMap<UndirectedGraphNode, UndirectedGraphNode>
I think it is better not to use global hashmap in practice. Here is my more general solution.
  • Only check for null in the initial call
  • Do not use global hash map
  • map node to cloned node instead of label to cloned node in case of duplicate labels
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
    if (node == null) return null;
    return cloneGraph(node, new HashMap<>());
}

private UndirectedGraphNode cloneGraph(UndirectedGraphNode node, Map<UndirectedGraphNode, UndirectedGraphNode> visited) {
    if (visited.containsKey(node)) return visited.get(node);
    
    UndirectedGraphNode clone = new UndirectedGraphNode(node.label);
    
    visited.put(node, clone);
    
    for (UndirectedGraphNode neighbor : node.neighbors)
        clone.neighbors.add(cloneGraph(neighbor, visited));
    
    return clone;
}
This question is the same as copy list with random pointer.
  1. The pivotal point to tackle this type of question is: to maintain a mapping between original data structure and the cloned version. Normally we use HashMap<...> to achieve this.
  2. Another key point to be noted is: Do you think it is better if we call map.clear() before we return the cloned data structure? It is a concern about garbage collection.
    Assumed We use quite often the cloneGraph(...) and copy thousands of GraphNode, it will cause the internal map to have a reference to all input parameters. And it will gradually lead to memory leak.
X. BFS
Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.
  • A queue is used to do breath first traversal.
  • a map is used to store the visited nodes. It is the map between original node and copied node.
http://www.cnblogs.com/yuzhangcmu/p/4044271.html
使用BFS来解决此问题。用一个Queue来记录遍历的节点,遍历原图,并且把复制过的节点与原节点放在MAP中防止重复访问。
图的遍历有两种方式,BFS和DFS
这里使用BFS来解本题,BFS需要使用queue来保存neighbors
但这里有个问题,在clone一个节点时我们需要clone它的neighbors,而邻居节点有的已经存在,有的未存在,如何进行区分?
这里我们使用Map来进行区分,Map的key值为原来的node,value为新clone的node,当发现一个node未在map中时说明这个node还未被clone,
将它clone后放入queue中处理neighbors。
使用Map的主要意义在于充当BFS中Visited数组,它也可以去环问题,例如A--B有条边,当处理完A的邻居node,然后处理B节点邻居node时发现A已经处理过了
处理就结束,不会出现死循环。
queue中放置的节点都是未处理neighbors的节点。
https://segmentfault.com/a/1190000003741052
广度优先搜索,同时用一个哈希表,将新旧节点映射起来。这样我们第一次遍历到的节点,我们会新建一个节点并映射到哈希表中。当以后再遍历到这个节点时,我们可以直接用哈希表取出它对应的新节点。我们只要保证,对于第一次遇到的图节点,我们都会建立一个克隆节点,并在哈希表映射起来就行了。
这里我们并不需要维护一个visited的集合,为什么呢?因为每次我们遇到一个之前没见过图节点,我们都会给它建立一个克隆节点,然后在哈希表中映射起来,并把这个图节点也放入队列中。所以只要哈希表中有这个图节点,就说明我们之前已经将该图节点放入队列了,就不需要再处理了。不过还是要把该图节点的克隆节点放入父克隆节点的邻居中。
https://discuss.leetcode.com/topic/18075/java-bfs-solution
public UndirectedGraphNode cloneGraph(UndirectedGraphNode root) {
  if (root == null) return null;
  
  // use a queue to help BFS
  Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>();
  queue.add(root);
  
  // use a map to save cloned nodes
  Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
  
  // clone the root
  map.put(root, new UndirectedGraphNode(root.label));
  
  while (!queue.isEmpty()) {
    UndirectedGraphNode node = queue.poll();
    
    // handle the neighbors
    for (UndirectedGraphNode neighbor : node.neighbors) {
      if (!map.containsKey(neighbor)) {
        // clone the neighbor
        map.put(neighbor, new UndirectedGraphNode(neighbor.label));
        // add it to the next level
        queue.add(neighbor);
      }
      
      // copy the neighbor
      map.get(node).neighbors.add(map.get(neighbor));
    }
  }
  
  return map.get(root);
}
https://discuss.leetcode.com/topic/4690/simple-java-iterative-bfs-solution-with-hashmap-and-queue
Use HashMap to look up nodes and add connection to them while performing BFS.
I agree with @rvanga that the above code will not be working with graph node having same labels, better go sure and use HashMap<UndirectedGraphNode, UndirectedGraphNode> .
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        if (node == null) return null;
        
        Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap(); // origin node : copied node
        UndirectedGraphNode myNode = new UndirectedGraphNode(node.label); // copy
        
        map.put(node, myNode);
        
        Queue<UndirectedGraphNode> q = new ArrayDeque(); // origin nodes
        q.add(node);
        
        // bfs traverse graph
        while (!q.isEmpty()) {
            UndirectedGraphNode cur = q.poll();
            // all neighbors of current origin node
            for (UndirectedGraphNode neighbor : cur.neighbors) {
                // if the origin node is not visited
                if (!map.containsKey(neighbor)) {
                    map.put(neighbor, new UndirectedGraphNode(neighbor.label));
                    // to avoid loop, we just add the unvisited node to queue
                    q.offer(neighbor);
                }
                // add neighbors to the copied node
                // copied node: map.get(cur) -> copied node of cur
                // neighbors: map.get(neighbor) -> copied node of neighbor
                map.get(cur).neighbors.add(map.get(neighbor));
            }
        }
        return myNode;
    }

From: http://leetcode.com/2012/05/clone-graph-part-i.html  we could use a hash table! As we copy a node, we insert it into the table. If we later find that one of a node’s neighbor is already in the table, we do not make a copy of that neighbor, but to push its neighbor’s copy to its copy instead. Therefore, the hash table would need to store a mapping of key-value pairs, where the key is a node in the original graph and its value is the node’s copy.
Node *clone(Node *graph) {
    if (!graph) return NULL;
    Map map;
    queue<Node *> q;
    q.push(graph);
    Node *graphCopy = new Node();
    map[graph] = graphCopy;
    while (!q.empty()) {
        Node *node = q.front();
        q.pop();
        int n = node->neighbors.size();
        for (int i = 0; i < n; i++) {
            Node *neighbor = node->neighbors[i];
            // no copy exists
            if (map.find(neighbor) == map.end()) {
                Node *p = new Node();
                map[node]->neighbors.push_back(p);
                map[neighbor] = p;
                q.push(neighbor);
            } else {     // a copy already exists
                map[node]->neighbors.push_back(map[neighbor]);
            }
        }
    }
    return graphCopy;
}
public class Solution {
    public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
        if(node == null)
            return null;
 
        LinkedList<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>();
        HashMap<UndirectedGraphNode, UndirectedGraphNode> map = 
                                   new HashMap<UndirectedGraphNode,UndirectedGraphNode>();
 
        UndirectedGraphNode newHead = new UndirectedGraphNode(node.label);
 
        queue.add(node);
        map.put(node, newHead);
 
        while(!queue.isEmpty()){
            UndirectedGraphNode curr = queue.pop();
            ArrayList<UndirectedGraphNode> currNeighbors = curr.neighbors; 
 
            for(UndirectedGraphNode aNeighbor: currNeighbors){
                if(!map.containsKey(aNeighbor)){
                    UndirectedGraphNode copy = new UndirectedGraphNode(aNeighbor.label);
                    map.put(aNeighbor,copy);
                    map.get(curr).neighbors.add(copy);
                    queue.add(aNeighbor);
                }else{
                    map.get(curr).neighbors.add(map.get(aNeighbor));
                }
            }
 
        }
        return newHead;
    }
}
EPI Java Solution: Clone a graph
clone-graph.cc CloneGraph.java
 public static class GraphVertex {
    public int label;
    public List<GraphVertex> edges;

    public GraphVertex(int label) {
      this.label = label;
      edges = new ArrayList<GraphVertex>();
    }
  }

  public static GraphVertex cloneGraph(GraphVertex g) {
    if (g == null) {
      return null;
    }

    Map<GraphVertex, GraphVertex> vertexMap = new HashMap<>();
    LinkedList<GraphVertex> q = new LinkedList<GraphVertex>();
    q.addLast(g);
    vertexMap.put(g, new GraphVertex(g.label));
    while (!q.isEmpty()) {
      GraphVertex v = q.removeFirst();
      for (GraphVertex e : v.edges) {
        // Try to copy vertex e.
        if (!vertexMap.containsKey(e)) {
          vertexMap.put(e, new GraphVertex(e.label));
          q.addLast(e);
        }
        // Copy edge v->e.
        vertexMap.get(v).edges.add(vertexMap.get(e));
      }
    }
    return vertexMap.get(g);
  }

2. 设计数据结构 a.
List<Node> b.
Map<Node,Set<Node>>
c.Matrix[][]

1. 写两种能够表示directed graph的数据结构,不不保证connected,每个
node的值不不保证unique。⽤的数据结构做LC窈伞霰。
2. 复制⼀一个联通的有向图,先Recursive地写了了⼀一遍,然后问能不不能
iterative地写,用了个queue⼜又写了了⼀一遍⾮非递归的。问了了我为什什么要⽤用
map不不⽤用set,然后问了了时间空间复杂度,node和edge都要考虑

https://github.com/mintycc/OnlineJudge-Solutions/blob/master/Leetcode/133_Clone_Graph.java

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts