Given a reference of a node in a connected undirected graph.
Return a deep copy (clone) of the graph.
Each node in the graph contains a value (int
) and a list (List[Node]
) of its neighbors.
class Node {
public int val;
public List<Node> neighbors;
}
For simplicity, each node's value is the same as the node's index (1-indexed). The graph is represented in the test case using an adjacency list.
An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.
The given node will always be the first node with val = 1
. You must return the copy of the given node as a reference to the cloned graph.
Example 1:
Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
Output: [[2,4],[1,3],[2,4],[1,3]]
Explanation: There are 4 nodes in the graph.
Example 2:
Input: adjList = [[]]
Output: [[]]
Explanation: The graph consists of only one node with val = 1 and it does not have any neighbors.
Example 3:
Input: adjList = []
Output: []
Explanation: This an empty graph, it does not have any nodes.
Constraints:
[0, 100]
.1 <= Node.val <= 100
Node.val
is unique for each node.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The goal is to create a complete copy of a graph. A brute force approach involves exploring every possible node and connection, systematically building the copy until it mirrors the original.
Here's how the algorithm would work step-by-step:
def clone_graph(node):
if not node:
return None
# Use a dictionary to store the mapping from original nodes to cloned nodes
node_map = {}
# Create a clone of the first node
cloned_node = Node(node.val)
node_map[node] = cloned_node
queue = [node]
while queue:
original_node = queue.pop(0)
# Iterate through the neighbors of the current node
for neighbor in original_node.neighbors:
# If the neighbor has not been cloned yet
if neighbor not in node_map:
# Create a clone of the neighbor
cloned_neighbor = Node(neighbor.val)
node_map[neighbor] = cloned_neighbor
queue.append(neighbor)
# Add the cloned neighbor to the neighbors of the cloned node
node_map[original_node].neighbors.append(node_map[neighbor])
return cloned_node
class Node:
def __init__(self, val, neighbors=None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
We want to make a perfect copy of a connected group of objects. The clever trick is to keep track of the objects we've already copied so we don't waste time re-copying them.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, val=0, neighbors=None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
def clone_graph(node: 'Node') -> 'Node':
if not node:
return None
cloned_nodes = {}
def clone_node(node):
if node in cloned_nodes:
return cloned_nodes[node]
# Create a clone for the current node
cloned_node = Node(node.val)
cloned_nodes[node] = cloned_node
return cloned_node
cloned_starting_node = clone_node(node)
queue = [node]
visited_nodes = {node}
while queue:
current_node = queue.pop(0)
current_cloned_node = cloned_nodes[current_node]
for neighbor_node in current_node.neighbors:
cloned_neighbor_node = clone_node(neighbor_node)
# Connect cloned node to its cloned neighbors
current_cloned_node.neighbors.append(cloned_neighbor_node)
# Avoid infinite loops by only visiting each node once.
if neighbor_node not in visited_nodes:
queue.append(neighbor_node)
visited_nodes.add(neighbor_node)
return cloned_starting_node
Case | How to Handle |
---|---|
Null input graph | Return null immediately indicating no graph to clone. |
Empty graph (graph with no nodes) | Return null, representing an empty clone. |
Graph with a single node | Create a new node with the same value and return it. |
Graph with multiple nodes and cycles | Use a visited set (or map) to prevent infinite recursion or loops during the graph traversal and cloning. |
Disconnected graph (multiple components) | The algorithm should correctly clone each disconnected component. |
Graph with a large number of nodes (scalability) | Ensure the algorithm uses iterative traversal (BFS or DFS) instead of recursion to avoid stack overflow errors, and that the space complexity is O(N) where N is the number of nodes. |
Self-loop (node connected to itself) | The algorithm should correctly clone the self-loop connection when creating the new node's neighbors. |
Nodes with different data types | Assume that all node values are of the same comparable type; otherwise, type handling logic would need to be added. |