SWE / MLE · Coding

Meta Clone Graph:身份映射、环与重边

复制起点可达图,保留自环、邻接顺序和重复边,避免相同值节点被合并。

题目重点:复制起点可达图,保留自环、邻接顺序和重复边,避免相同值节点被合并。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • Node以对象身份区分,value可以重复;neighbors有序列表可含重边和自环。只复制start可达component,空start返回None。

解题路线

  1. map从原Node到新Node,以起点创建副本并入队。处理每条neighbor边,未见节点先创建并记录,再入队;无论是否已见都向当前副本邻接表追加对应副本。
  2. 先登记再遍历阻止环造成无限递归/重复创建;如果只在处理完邻居后登记,A↔B会不断相互克隆。BFS与DFS均可,但递归深度风险不同。
  3. 不要以value为map key,不同原节点可能都值1;不要用set替代neighbors,否则重边/原顺序丢失。单个原对象出现多处都必须指同一副本。
  4. 复制可达component,不遍历一个题目没提供的全图节点集合。若要求所有component,需要外部nodes输入,逐个未见起点遍历并共享map。
  5. 独立验证把原图转为索引邻接,按边顺序建立原索引→副本映射,检查一一对应和完全无原对象引用;只比较序列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对象身份时,需要增加怎样的映射?
✓已复制微信号!