r"""
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 node 0 to both nodes 1 and 2.
Second node is labeled as 1. Connect node 1 to node 2.
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
         / \
         \_/
"""
import collections


class UndirectedGraphNode:
    """
    A node in an undirected graph. Contains a label and a list of neighbouring
    nodes (initially empty).
    """

    def __init__(self, label):
        self.label = label
        self.neighbors = []

    def shallow_copy(self):
        """
        Return a shallow copy of this node (ignoring any neighbors)
        """
        return UndirectedGraphNode(self.label)

    def add_neighbor(self, node):
        """
        Adds a new neighbor
        """
        self.neighbors.append(node)


def clone_graph1(node):
    """
    Returns a new graph as seen from the given node using a breadth first search (BFS).
    """
    if not node:
        return None
    node_copy = node.shallow_copy()
    dic = {node: node_copy}
    queue = collections.deque([node])
    while queue:
        node = queue.popleft()
        for neighbor in node.neighbors:
            if neighbor not in dic:  # neighbor is not visited
                neighbor_copy = neighbor.shallow_copy()
                dic[neighbor] = neighbor_copy
                dic[node].add_neighbor(neighbor_copy)
                queue.append(neighbor)
            else:
                dic[node].add_neighbor(dic[neighbor])
    return node_copy


def clone_graph2(node):
    """
    Returns a new graph as seen from the given node using an iterative depth first search (DFS).
    """
    if not node:
        return None
    node_copy = node.shallow_copy()
    dic = {node: node_copy}
    stack = [node]
    while stack:
        node = stack.pop()
        for neighbor in node.neighbors:
            if neighbor not in dic:
                neighbor_copy = neighbor.shallow_copy()
                dic[neighbor] = neighbor_copy
                dic[node].add_neighbor(neighbor_copy)
                stack.append(neighbor)
            else:
                dic[node].add_neighbor(dic[neighbor])
    return node_copy


def clone_graph(node):
    """
    Returns a new graph as seen from the given node using a recursive depth first search (DFS).
    """
    if not node:
        return None
    node_copy = node.shallow_copy()
    dic = {node: node_copy}
    dfs(node, dic)
    return node_copy


def dfs(node, dic):
    """
    Clones a graph using a recursive depth first search. Stores the clones in
    the dictionary, keyed by the original nodes.
    """
    for neighbor in node.neighbors:
        if neighbor not in dic:
            neighbor_copy = neighbor.shallow_copy()
            dic[neighbor] = neighbor_copy
            dic[node].add_neighbor(neighbor_copy)
            dfs(neighbor, dic)
        else:
            dic[node].add_neighbor(dic[neighbor])