133. Clone Graph
Tags: javascript, programming, tutorial, career
Published: 2022-06-08 13:24:02

***
### Solution Developed In:
![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E)

## The Question
For this article we will be covering Leetcode's '[133. Clone Graph](https://leetcode.com/problems/clone-graph/)' question. 

Question:

> 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**.
> 
>![Example](https://assets.leetcode.com/uploads/2019/11/04/133_clone_graph_question.png)

```
class Node {
    public int val;
    public List<Node> neighbors;
}
```

## Explaining The Question
This Question is rated **Medium**. Which I believe is entirely accurate, so long as you have solid foundations in [Graph Theory](https://en.wikipedia.org/wiki/Graph_theory) and it's applications. As well as ideally being skilled in either [DFS](https://en.wikipedia.org/wiki/Depth-first_search) or [BFS](https://en.wikipedia.org/wiki/Breadth-first_search) algorithms.

Without knowing [DFS](https://en.wikipedia.org/wiki/Depth-first_search) or [BFS](https://en.wikipedia.org/wiki/Breadth-first_search) algorithms, you will be unable to solve this problem. 😁

What we're being asked to do is to clone a graph. The idea is to create a new graph with the same nodes and edges as the original graph, but with the same values and neighbors WITHOUT referencing the original input graph. 


***

##  Recommended Knowledge
1. [Graph Theory](https://en.wikipedia.org/wiki/Graph_theory#:~:text=In%20mathematics%2C%20graph%20theory%20is,also%20called%20links%20or%20lines)
2. [Adjacency List](https://en.wikipedia.org/wiki/Adjacency_list)
3. [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search) (Recursive)
4. [Breadth First Search](https://en.wikipedia.org/wiki/Breadth-first_search) (Optional)
5. [Hash Map](https://en.wikipedia.org/wiki/Hash_table)

## What do we know?
1. We're given a reference to a node in a connected undirected graph that is represented with as an [adjacency list](https://en.wikipedia.org/wiki/Adjacency_list) in the `input` section. 
2. We need to create a new graph with the same nodes and edges as the original graph, but with the same values and neighbors WITHOUT referencing the original input graph.

## How we're going to do it:

We're going to recursively create this graph. The way we're going to do this is by using conducting a **Depth First Search** on the graph, at each `node` we visit we will create our own node (`root`) that is a copy of said node, and we will then add the old node to a hashmap (`visited_nodes`) to keep track of the nodes we've already visited. So we can prevent the duplication of nodes. Sometimes this is called [Memoization](https://en.wikipedia.org/wiki/Memoization).

At each node that we have not seen in the `visited_nodes` before we create it, we then conduct the same DFS on the `neighbors` of that node, which will return us a set of new nodes that we will add to the `root` node's `neighbors` list.

1. We're going to firstly create a `visited_nodes` hashmap to keep track of all of our visited nodes
2. We will then perform a Depth First Search (`copy_graph`) on all the nodes in the `input` graph, and we will create a new node for each node in the `input` graph.
3. In this Depth First Search we firstly ask 'Have we been to this node before?' if we have we will return the node that we have already created, if we have not we will create a new node and add it to the `visited_nodes` hashmap. This will make sense in just a second
4. So at this point, we have not visited this node, so we create it, and then we conduct a Depth First Search on the `neighbors` of this node, and we add the new nodes to the `neighbors` list of the `root` node.
5. Once the Depth First Search has been conducted on all the `neighbors` of the `root` node, we return the `root` node.
6. We do this to always return that newly created node, so we can add it to the `neighbors` list if called upon. 

## Big O Notation:
* Time Complexity:   *O(**V + E**)* / *O(**n**)* | Where ***n*** is the number of nodes in the Matrix. ***V*** is the number of vertices in the graph. ***E*** is the number of edges in the graph as we're going to visit each vertex and each edge once. This is often represented as just **O(*n*)** as it's the number of nodes in the graph.
* Space Complexity: *O(**n**)* | Where ***n*** is the number of nodes in the `Input` graph as we will be using a hashmap to keep track of all the nodes we've already visited and at the same time storing the nodes of a given graph in the Call Stack due to the recursive Depth First Search.

The Big O Notation analysis for this problem is a little confusing as it combines a handful of different concepts all with their own Big O Notations. But in general, we reduce it down to the worst complexity being *O(**n**)*. Feel free to correct me if you feel my analysis is wrong.

## Leetcode Results:

See Submission Link: 

* Runtime: 71  ms, faster than ***79.16%*** of JavaScript online submissions for Clone Graph
* Memory Usage: 43.8 MB, less than ***60.27%*** of JavaScript online submissions for Clone Graph.

[![LeetCode](https://img.shields.io/badge/LeetCode-000000?style=for-the-badge&logo=LeetCode&logoColor=#d16c06)](https://leetcode.com/submissions/detail/717316988/)


***

# The Solution
```
/**
 * Definition for a Node.
 * function Node(val, neighbors) {
 *    this.val = val === undefined ? 0 : val;
 *    this.neighbors = neighbors === undefined ? [] : neighbors;
 * };
 */

/**
 * @param {Node} node
 * @return {Node}
 */
var cloneGraph = function (node) {

    // Edge case 🤮
    // Empty node.
    if (!node) {
        return null;
    }

    // As all the nodes values are unique, we will use a hashmap
    // to store the visited nodes on the old graph.
    const node_map = new Map(); // Node.val -> Newly Created Node Address

    // We're going to recursively traverse the old graph and create new nodes
    // on the new graph. We do this by asking our hashmap if the current node
    // we plan on creating already exists within the hashmap? Meaning, we have already visited it.
    // If this is the case, we return the node already created.
    // If not, we create a new Node and add it to the hashmap (To prevent us from creating the same node twice).
    // We then iterate over all of the current nodes neighbors and recursively call our function.
    // Then we set it's returned result (Being either a new node or an already created node) as the current node's neighbors.
    const create_graph = (node) => {
 
        // Have we already visited this node?
        // If so return the node we already created.
        if (node_map.has(node.val)) {
            return node_map.get(node.val);
        }

        // So we have never visited this node before,
        // so create it, and add it to the hashmap to prevent us from creating it again.
        const root = new Node(node.val, []);
        node_map.set(node.val, root);

        // We now need to get all of this nodes neighbors for a fully deep clone.
        // So we're going to recursively call our function on each of the current nodes neighbors.
        // So it will create all of it's neighbors and then set them as the current node's neighbors.
        for (const neighbors of node.neighbors) {

            // Get my neighbors nodes, by creating them
            // or returning them if they already exist.
            const near_nodes = create_graph(neighbors);
            root.neighbors.push(near_nodes);
        }

        // Return the newly created node.
        // It can return at any point. 
        return root;
    };

    return create_graph(node);
};

```