Given a node in a connected undirected graph, return a deep copy of the graph.
Each node in the graph contains an integer value and a list of its neighbors.
class Node {
public int val;
public List<Node> neighbors;
}The graph is shown in the test cases as an adjacency list. An adjacency list is a mapping of nodes to lists, used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.
For simplicity, nodes values are numbered from 1 to n, where n is the total number of nodes in the graph. The index of each node within the adjacency list is the same as the node's value (1-indexed).
The input node will always be the first node in the graph and have 1 as the value.
Example 1:
Input: adjList = [[2],[1,3],[2]]
Output: [[2],[1,3],[2]]Explanation: There are 3 nodes in the graph.
Node 1: val = 1 and neighbors = [2].
Node 2: val = 2 and neighbors = [1, 3].
Node 3: val = 3 and neighbors = [2].
Example 2:
Input: adjList = [[]]
Output: [[]]Explanation: The graph has one node with no neighbors.
Example 3:
Input: adjList = []
Output: []Explanation: The graph is empty.
Constraints:
0 <= The number of nodes in the graph <= 100.1 <= Node.val <= 100
You should aim for a solution with O(V + E) time and O(E) space, where V is the number of vertices and E is the number of edges in the given graph.
We are given only the reference to the node in the graph. Cloning the entire graph means we need to clone all the nodes as well as their child nodes. We can't just clone the node and its neighbor and return the node. We also need to clone the entire graph. Can you think of a recursive way to do this, as we are cloning nodes in a nested manner? Also, can you think of a data structure that can store the nodes with their cloned references?
We can use the Depth First Search (DFS) algorithm. We use a hash map to map the nodes to their cloned nodes. We start from the given node. At each step of the DFS, we create a node with the current node's value. We then recursively go to the current node's neighbors and try to clone them first. After that, we add their cloned node references to the current node's neighbors list. Can you think of a base condition to stop this recursive path?
We stop this recursive path when we encounter a node that has already been cloned or visited. This DFS approach creates an exact clone of the given graph, and we return the clone of the given node.
Before attempting this problem, you should be comfortable with:
The graph may contain cycles, so we cannot simply copy nodes recursively without remembering what we've already copied.
To handle this, we use a map (old → new):
Depth First Search (DFS) helps us explore and clone all connected nodes.
null, return null."""
# Definition for a Node.
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: Optional['Node']) -> Optional['Node']:
oldToNew = {}
def dfs(node):
if node in oldToNew:
return oldToNew[node]
copy = Node(node.val)
oldToNew[node] = copy
for nei in node.neighbors:
copy.neighbors.append(dfs(nei))
return copy
return dfs(node) if node else NoneWhere is the number of vertices and is the number of edges.
The graph can have cycles, so while cloning we must avoid creating duplicate nodes or looping forever.
Using Breadth First Search (BFS), we explore the graph level by level and keep a map from original nodes to their clones.
null, return null."""
# Definition for a Node.
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: Optional['Node']) -> Optional['Node']:
if not node:
return None
oldToNew = {}
oldToNew[node] = Node(node.val)
q = deque([node])
while q:
cur = q.popleft()
for nei in cur.neighbors:
if nei not in oldToNew:
oldToNew[nei] = Node(nei.val)
q.append(nei)
oldToNew[cur].neighbors.append(oldToNew[nei])
return oldToNew[node]Where is the number of vertices and is the number of edges.
Failing to track visited nodes causes infinite recursion when the graph contains cycles. Always use a map to store already-cloned nodes before processing neighbors.
# Wrong: No visited tracking
def dfs(node):
copy = Node(node.val)
for nei in node.neighbors:
copy.neighbors.append(dfs(nei)) # Infinite loop on cycles
return copyWithout a mapping from original to cloned nodes, the same node may be cloned multiple times, breaking the graph structure. Two neighbors pointing to the same original node should point to the same cloned node.
# Wrong: Creates duplicate clones
if nei not in oldToNew:
oldToNew[nei] = Node(nei.val)
# Must add to map BEFORE recursing to handle back-edgesThe clone must be added to the map immediately after creation, before recursing on neighbors. Otherwise, back-edges to the current node will create a new clone instead of reusing the existing one.