πŸ“Š133. Clone Graph

Medium | Grind 75 | July 12 2022

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;
}

Test case format:

For simplicity, each node's value is the same as the node's index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. 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.

Constraints:

  • The number of nodes in the graph is in the range [0, 100].

  • 1 <= Node.val <= 100

  • Node.val is unique for each node.

  • There are no repeated edges and no self-loops in the graph.

  • The Graph is connected and all nodes can be visited starting from the given node.

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.
1st node (val = 1)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
2nd node (val = 2)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).
3rd node (val = 3)'s neighbors are 2nd node (val = 2) and 4th node (val = 4).
4th node (val = 4)'s neighbors are 1st node (val = 1) and 3rd node (val = 3).

SOLUTION

The main task here is to traverse the graph and at each node, create new nodes since we need to make deep copy. Deep copy has to store the objects actual value where as shallow copy we basically store the referenceof objects to the original memory (geeksforgeeks).

There are two ways to solve this problem. Breath First Search (BFS) vs Depth First Search (DFS). When I solved this initially, I used DFS since that was more intuitive for me. However, after exploring a solution using BFS, that was much nicer and easier since it can be done iteratively.

1. DEPTH FIRST SEARCH

For the following solution, we will first look at the first node. We make a copy of that node and look at the neighbors. When we look at the neighbors we take the first neighbors and recusively go through its neighbor and its neighbor and so on.

Example 1: In the above example, we will first look at node 1. Once we create node 1 and store in our output dictionary, we will move to its neighbor. When look at the neighbor, we will first encounter 2. Before we can process neighbor 4, we have go down the rabbit hole of node 2. We look at the neighbor of node 2 which is 1 and 3. We will encounter Node 1 first, but since we saw node 1 already, we return and we will now look at node 3. We create a new node with value 3 and iterate over its neighbors which are 2 and 4. We first look at Node 2, and since we saw it earlier, we return. We now move to Node 4. We create a new Node with value 4 and look at its neighbors which are 1 and 3. since we already saw them, we return. this can triggers the return each recursive calls since we have seen and copied all the nodes.

2. BREATH FIRST SEARCH

The workings of BFS is similar to DPS but here we look at each level at a time. Instead of going through the rabbit hole of a neighbor, then the neighbor of the neighbor and again its neighbor, in BFS, we will tackle all the neighbors of the first node. Then move to the next node and tackle all its neighbors.

For the solution below, we will use a dictionary to store the newly created nodes and an array acting like a queue to contain the nodes that needs to be processed. We append the first node to our queue so that our queue is not empty. Then we get into the loop and pop the first node. we create a new node with the corresponding value and iterate over its neighbors. If we are seeing the neighbors for the first time, we create a new Node with their corresponding value and then add them to the queue so they can be processed. Processed here basically means append appropiate neighbors for the node.

This process will methodically go through the graph level wise. In example 1, the queue will first process Node 1. While processing this it will add Node 2 and Node 4 to the queue. It then prcoesses Node 2 and then Node 4. When processing Node 2, Node 3 will be added to the queue. However, Node 3 wont be processed until we have finished processed Node 4 since that was seen first.

TIME AND SPACE COMPLEXITY Time complexity: For both DFS and BFS, we go through every single node once in the graph, so therefore the time complexity is O(v + e) where v is the number of vertices and e is the edges.

Space Complexity: For Both DFS and BFS, we use a dictionary to hold all the nodes. The size of the dictionary is the number of nodes since we have to store all the unique nodes in it. Therefore the space complexity is O(v) where v is the number of nodes

Last updated