Depth-First Search
Copy Graph
DESCRIPTION
Given a reference to a variable node which is part of an undirected, connected graph, write a function that returns an adjacency list representation of the graph in dictionary form. The keys of the adjacency list are the values of the nodes, and each value is a list of that node's neighbors' values.
This isn't a deep copy of the node objects. You're converting the node-and-pointers structure into a dictionary adjacency list that describes the same graph.
node is an instance of the following class, where neighbors is a list of references to other nodes in the graph (also of type IntGraphNode):
class IntGraphNode: def __init__(self, value = 0, neighbors = None): self.value = value self.neighbors = neighbors if neighbors is not None else []
Example 1:
Input:
node = IntGraphNode(1, [IntGraphNode(2), IntGraphNode(3)])
Output:
>>> copy_graph(node) {1: [2, 3], 2: [1], 3: [1]}
Example 2: Input:
n1 = IntGraphNode(1) n2 = IntGraphNode(2) n3 = IntGraphNode(3) n4 = IntGraphNode(4) n1.neighbors = [n2, n4] n2.neighbors = [n1, n3] n3.neighbors = [n2, n4] n4.neighbors = [n1, n3]
Output:
>>> copy_graph(n1) {1: [2, 4], 2: [1, 3], 3: [2, 4], 4: [1, 3]}
// class IntGraphNode {
// int value;
// IntGraphNode[] neighbors;
// }
public class Solution {
public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {
// Your code here
}
}Run your code to see results here
Have suggestions or found something wrong?
Explanation
This solution uses depth-first search to traverse each node in the original graph. We can define a recursive helper function dfs that takes in an input node to help us perform the DFS traversal.
At each node, we:
- Add the value of the node as a key in the adjacency list, and a list of its neighbor's values as the value in the dictionary.
- Recursively call the dfs function on each neighbor of the node.
The adjacency list also helps us keep track of visited nodes. If we call dfs on a node that has already been added to the adjacency list, this means we have already visited the node, so we can return right away before making any more recursive calls.
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
We can now take a closer look at the solution by visualizing each step as it traverses the graph below:
Initialization
The first step is to initialize adj_list as an empty dictionary. We will return this dictionary at the end, after the depth-first search traversal is complete. We then define the recursive helper function dfs that takes a node as input, and make the initial call to dfs with the input node.
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
copy graph
0 / 2
Defining both adj_list and the helper dfs function inside the main function ensures us that:
- each call to the recursive function can access adj_list directly
- the scope of adj_list is limited to the main function, which means that other parts of the code cannot modify it.
Depth-First Search
When the dfs function is called with a node, it first checks if the node is already present in the adj_list dictionary. If it isn't, it adds the node to the dictionary. The key is the value of the node, and the value is a list of the values of each of the node's neighbors.
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}
recursive call
0 / 1
Then, it recursively calls dfs on each neighbor of the original node.
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}
add to adj_list
0 / 5
When dfs is called on a node that is already present in adj_list, it returns immediately without making any more recursive calls, which helps us avoid infinite loops in the graph. After returning, the function continues to the next neighbor of the current node.
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}
private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}
private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}
recursive call
0 / 2
This process continues until all nodes in the original graph have been visited and added to the adj_list dictionary.
Animated Solution
// class IntGraphNode {// int value;// IntGraphNode[] neighbors;// }class Solution {public Map<Integer, List<Integer>> copy_graph(IntGraphNode node) {Map<Integer, List<Integer>> adjList = new HashMap<>();if (node != null) {dfs(node, adjList);}return adjList;}private void dfs(IntGraphNode node, Map<Integer, List<Integer>> adjList) {if (adjList.containsKey(node.value)) {return;}List<Integer> neighborValues = new ArrayList<>();for (IntGraphNode neighbor : node.neighbors) {neighborValues.add(neighbor.value);}adjList.put(node.value, neighborValues);for (IntGraphNode neighbor : node.neighbors) {dfs(neighbor, adjList);}}}
copy graph
0 / 26
What is the time complexity of this solution?
O(2ⁿ)
O(N + M)
O(1)
O(n * logn)