题目重点:复制起点可达图,保留自环、邻接顺序和重复边,避免相同值节点被合并。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- Node以对象身份区分,value可以重复;neighbors有序列表可含重边和自环。只复制start可达component,空start返回None。
解题路线
- map从原Node到新Node,以起点创建副本并入队。处理每条neighbor边,未见节点先创建并记录,再入队;无论是否已见都向当前副本邻接表追加对应副本。
- 先登记再遍历阻止环造成无限递归/重复创建;如果只在处理完邻居后登记,A↔B会不断相互克隆。BFS与DFS均可,但递归深度风险不同。
- 不要以value为map key,不同原节点可能都值1;不要用set替代neighbors,否则重边/原顺序丢失。单个原对象出现多处都必须指同一副本。
- 复制可达component,不遍历一个题目没提供的全图节点集合。若要求所有component,需要外部nodes输入,逐个未见起点遍历并共享map。
- 独立验证把原图转为索引邻接,按边顺序建立原索引→副本映射,检查一一对应和完全无原对象引用;只比较序列value无法验证cycle/self-loop。
手动走一遍
A、B都value1,A.neighbors=[A,B,B],B.neighbors=[A]。克隆后A′邻居为[A′,B′,B′],A′和B′不同对象,B′返回A′;重边不能消掉。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from collections import deque
class Node:
def __init__(self,value):self.value=value;self.neighbors=[]
def clone_graph(start):
"""Copy reachable component; object identity keys, ordered adjacency preserved."""
if start is None:return None
clones={start:Node(start.value)};queue=deque([start])
while queue:
node=queue.popleft()
for neighbor in node.neighbors:
if neighbor not in clones:
clones[neighbor]=Node(neighbor.value);queue.append(neighbor)
clones[node].neighbors.append(clones[neighbor])
return clones[start]
复杂度
V可达节点、E邻接项,时间O(V+E)、map与queue O(V),输出O(V+E)。无向图每条边通常在邻接表记两次,渐进仍O(V+E)但容量应按实际列表项计。
易错点与追问
容易失分的边界
- 用value查重会合并不同节点。
- 把只有新发现节点才追加邻接,已有节点的回边全部丢失。
继续深挖
- 节点额外payload是可变嵌套对象时,deepcopy范围怎样定?
- 保持边权、parallel edges与edge对象身份时,需要增加怎样的映射?
