3 minute read

Clone graph.

Problem description

Taken directly from LeetCode.

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 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.

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.
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).

Example 2:

Input: adjList = [[]]
Output: [[]]
Explanation: Note that the input contains one empty list. 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:

  • 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.

Solution type

Replication (I mean, it’s in the title). Actual strategy to replicate is BFS.

Solution

This has to be a problem that I’ve done before in class. I don’t think I remember the solution exactly, but what immediately comes to mind is BFS / DFS (take your pick) and a map of original node index => pointer to deep copy of that node. On each iteration, consider the next node in the search data structure; make a deep copy of it, store the index / deep copy pointer pair in the map, and for all nodes in its neighbors list that aren’t already in the map, add to the search data structure. Repeat until the search data structure is clear.

For some reason I feel like there’s a way to do this without a map? If it were a binary tree then sure, but I think for a general graph you might need to do otherwise? It’s not correct to pull out an MST algorithm or Dijkstra’s here because we’re not dealing with paths or anything like that. I’ll implement it first and try to optimize later if possible.

Other question: how the heck do I do this in Python? I can do this quickly in C++, but I’m rusty on Python OOP. Nothing a little Googling and guess-and-check can’t fix, though…

5 seconds after typing that, I realized that it’s more or less the same as what I’ve done in past Python projects. I make myself feel silly sometimes.

Okay, I ended up sticking with my original idea. I see no alternative to storing a map because you have to be able to avoid repeating work if you stumble upon a previously seen node. The main hiccup I had was actually to do with my BFS implementation, not the problem itself.

def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
    if not node:
        return None
    
    root = Node(node.val, [])
    map = {node: root}
    bfs = [node]
    while len(bfs) != 0:
        old_current_node = bfs[0] 
        new_current_node = map[old_current_node]
        for neighbor in old_current_node.neighbors:
            if neighbor not in map.keys():                    
                new_neighbor = Node(neighbor.val, [])
                map[neighbor] = new_neighbor
                bfs.append(neighbor)
            new_current_node.neighbors.append(map[neighbor])
        bfs.pop(0)
    return root

Funnily enough, I spent a bunch more time on this problem than I originally intended because the leetcode site went down for about 10 minutes. There’s a mean joke here about the leetcode engineers doing too much leetcode instead of actual useful engineering, but then again here I am, so…

Leave a comment

Your email address will not be published. Required fields are marked. *

Loading...