1 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 #
.
- First node is labeled as
0
. Connect node0
to both nodes1
and2
. - Second node is labeled as
1
. Connect node1
to node2
. - Third node is labeled as
2
. Connect node2
to node2
(itself), thus forming a self-cycle.
Visually, the graph looks like the following:
1 / / 0 --- 2 / \_/
解析:
1 先复制节点,再复制节点的neighbors关系。
2 label是唯一的,可以用label作为Map的key,以确定哪些节点已经复制。
代码如下:
1 package leetcode; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.HashSet; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Set; 9 10 public class CloneGraph { 11 class UndirectedGraphNode { 12 int label; 13 List<UndirectedGraphNode> neighbors; 14 Set<UndirectedGraphNode> set = new HashSet<CloneGraph.UndirectedGraphNode>(); 15 16 UndirectedGraphNode(int x) { 17 label = x; 18 neighbors = new ArrayList<UndirectedGraphNode>(); 19 } 20 21 public String toString(Set<UndirectedGraphNode> set) { 22 if (set.contains(this)) { 23 return ""; 24 } 25 set.add(this); 26 27 String s = label + ","; 28 for (UndirectedGraphNode neighbor : neighbors) { 29 s += neighbor.label + ","; 30 } 31 s += "#"; 32 33 for (UndirectedGraphNode neighbor : neighbors) { 34 s += neighbor.toString(set); 35 } 36 return s; 37 } 38 39 @Override 40 public String toString() { 41 set.clear(); 42 return toString(set); 43 } 44 }; 45 46 private void cloneNode(UndirectedGraphNode node, Map<Integer, UndirectedGraphNode> map) { 47 48 if (map.containsKey(node.label)) { 49 return; 50 } 51 52 UndirectedGraphNode nodeClone = new UndirectedGraphNode(node.label); 53 map.put(node.label, nodeClone); 54 for (UndirectedGraphNode neighbor : node.neighbors) { 55 cloneNode(neighbor, map); 56 } 57 return; 58 } 59 60 private void cloneNeighbors(UndirectedGraphNode node, UndirectedGraphNode nodeClone, Map<Integer, UndirectedGraphNode> map) { 61 62 if (node.neighbors.size() == nodeClone.neighbors.size()) { 63 return; 64 } 65 66 for (UndirectedGraphNode neighbor : node.neighbors) { 67 nodeClone.neighbors.add(map.get(neighbor.label)); 68 } 69 70 for (UndirectedGraphNode neighbor : node.neighbors) { 71 cloneNeighbors(neighbor, map.get(neighbor.label), map); 72 } 73 } 74 75 public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { 76 if (node == null) { 77 return null; 78 } 79 80 // 复制节点 81 Map<Integer, UndirectedGraphNode> map = new HashMap<Integer, UndirectedGraphNode>(); 82 cloneNode(node, map); 83 84 UndirectedGraphNode nodeClone = map.get(node.label); 85 86 // 复制neighbors关系 87 cloneNeighbors(node, nodeClone, map); 88 89 return nodeClone; 90 } 91 }
2 题外话
本题有坑:
(1) 无向图,node2是node1的neighbors,不代表node1是node2的neighbors。
本来是准备将nodeClone挂到node的最后一个neighbors,然后复制nodeClone的neighbors关系,再断开node和nodeClone的连接。
这样时间复杂度为O(n),空间复杂度O(1)。
这么做的瓶颈在于最后无法区分哪些是node的节点,哪些是clone的节点。这个需要存在如下假设:有node1.neighbors.contains(node2),则必有node2.neighbors.contains(node1)。
(2) 本题label唯一不通用,可以取node作为Map的key,这就需要为UndirectedGraphNode添加node的hashCode()和equals()方法,而leetcode是不允许修改这个的。一方面,平时我们是可以修改类的;另一方面,我们可以创建UndirectedGraphNode的子类,计算完成后,将结果强转为父类。