1.题目描述
链接:https://leetcode.cn/problems/clone-graph/
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
# your code
克隆一个用邻接表存储的图。输入为图的一个节点,包含值val和一个存储所有邻居节点的列表neighbors,输出为克隆之后的的输入节点。
2.图的BFS遍历
想要解决这道题,首先得知道图的BFS遍历,下面给出图BFS遍历的模板,理解记忆即可:
def bfs_graph_templete(self, root: Node) -> List[Node]:
"""
这里获取所有图中与root联通的节点,无序
"""
if not root:
return []
queue = collections.deque([root])
visited = set([root]) # 记录已经访问的节点
while queue:
node = queue.popleft()
# 将节点出队后,进行处理
# ...
for neighbor in node.neighbors:
if neighbor in visited:
continue
visited.add(neighbor)
queue.append(neighbor)
return list(visited)
可以看到,通过一个集合visited来存储已经访问过的节点,且只将未访问的节点加入队列queue,避免死循环。加入queue表示存储在队列中之后将会被访问,为此加入队列的同时将节点标记为已访问。
通过一下示例代码可以得到包含节点1,2,3,4的简单图的所有节点。
if __name__ == '__main__':
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node4 = Node(4)
node1.neighbors.append(node2)
node1.neighbors.append(node4)
node2.neighbors.append(node1)
node2.neighbors.append(node3)
node3.neighbors.append(node2)
node3.neighbors.append(node4)
node4.neighbors.append(node1)
node4.neighbors.append(node3)
root = node1
nodes = bfs_graph_templete(root)
res = [node.val for node in nodes]
print(res)
# outputAA
[3, 4, 2, 1]
3.解法一:BFS边遍历边复制
通过BFS按层遍历节点,使用字典visited存储已访问节点并保存原始节点与拷贝节点的对应关系。找到一个节点后,也就得到了它的邻居节点,再得到对应的拷贝节点,就可以将边复制到拷贝的图中。要注意的是,访问过的邻居不能直接跳过,因为它还是其他节点的邻居。
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return None
root = Node(node.val)
queue = collections.deque([node])
visited = {node: root}
while queue:
node_cur = queue.popleft()
node_cpy = visited[node_cur]
for neighbor in node_cur.neighbors:
if neighbor not in visited:
neighbor_cpy = Node(neighbor.val)
else:
neighbor_cpy = visited[neighbor]
# process
node_cpy.neighbors.append(neighbor_cpy)
if neighbor not in visited:
visited[neighbor] = neighbor_cpy
queue.append(neighbor)
return root
4.解法二:BFS复制所有节点,再复制所有边
这种解法先获取所有的节点,再对所有的节点进行复制,并保留对应关系,最后根据原始图的边,在克隆图中添加边。低耦合、清晰的解法,值得学习。
class Solution:
def cloneGraph(self, node: 'Node') -> 'Node':
if not node:
return None
nodes = self.find_all_nodes(node)
mapping = self.copy_nodes(nodes)
self.copy_edges(nodes, mapping)
return mapping[node]
def find_all_nodes(self, root):
"""
找到所有的节点,无序
"""
if not root:
return []
queue = collections.deque([root])
visited = set([root])
while queue:
node = queue.popleft()
for neighbor in node.neighbors:
if neighbor in visited:
continue
visited.add(neighbor)
queue.append(neighbor)
return list(visited)
def copy_nodes(self, nodes):
"""
复制所有的节点
"""
mapping = {}
for node in nodes:
mapping[node] = Node(node.val)
return mapping
def copy_edges(self, nodes, mapping):
"""
复制所有的边
"""
for node in nodes:
for neighbor in node.neighbors:
mapping[node].neighbors.append(mapping[neighbor])