Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@
* [Ordered Dict](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/dicts/ordered_dict.py)
* Graphs
* [Edge](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/edge.py)
* Undirected
* Clone Graph
* [Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/undirected/clone_graph/node.py)
* [Vertex](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/graphs/vertex.py)
* Hashmap
* [Test Hashmap](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/hashmap/test_hashmap.py)
Expand Down
101 changes: 101 additions & 0 deletions datastructures/graphs/undirected/clone_graph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Clone Graph

You are given a reference to a single node in an undirected, connected graph. Your task is to create a deep copy of the
graph starting from the given node. A deep copy means creating a new instance of every node in the graph with the same
data and edges as the original graph, such that changes in the copied graph do not affect the original graph.

Each node in the graph contains two properties:

- `data`: The value of the node, which is the same as its index in the adjacency list.
- `neighbors`: A list of connected nodes, representing all the nodes directly linked to this node.

However, in the test cases, a graph is represented as an adjacency list to understand node relationships, where each
index in the list represents a node (using 1-based indexing). For example, for [[2,3],[1,3],[1,2]], there are three
nodes in the graph:

1st node (data = 1): Neighbors are 2nd node (data = 2) and 3rd node (data = 3).
2nd node (data = 2): Neighbors are 1st node (data = 1) and 3rd node (data = 3).
3rd node (data = 3): Neighbors are 1st node (data = 1) and 2nd node (data = 2).

The adjacency list will be converted to a graph at the backend and the first node of the graph will be passed to your
code.

## Constraints

- 0 ≤ Number of nodes ≤ 100
- 1 ≤ Node.data ≤ 100
- Node.data is unique for each node.
- The graph is undirected, i.e., there are no self-loops or repeated edges.
- The graph is connected, i.e., any node can be visited from a given node.

## Topics

- Hash Table
- Depth-First Search
- Breadth-First Search
- Graph Theory

## Examples

![Example 1](./images/examples/clone_graph_example_1.png)

> 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](./images/examples/clone_graph_example_2.png)

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

## Solution

We use depth-first traversal and create a copy of each node while traversing the graph. We use a hash map to store each
visited node to avoid getting stuck in cycles. We do not revisit nodes that exist in the hash map. The hash map key is
a node in the original graph, and its value is the corresponding node in the cloned graph.

In order to solve this problem using the methodology discussed above, we create a recursive function, clone_helper, that
takes two arguments: the current node being cloned and a hash map. The steps of this recursive function are given below:

1. If graph is empty, return NULL. This will also work as a base case for our recursive function.
2. If the current node is not NULL, create a new Node with the same data as the current node, and add the current node
as key and its clone as value to the hash map.
3. Iterate through all the neighbors of the current node. For each neighbor, check if the neighbor is already cloned by
looking up the neighbor in the hash map:
- If the neighbor is not cloned yet, recursively call the function with the neighbor as the current node.
- If the neighbor is already cloned, add the cloned neighbor to the new node’s neighbors.
4. Finally, return the new node.

The clone function is the main function that creates a deep copy of the graph. It takes a single argument, which is a
reference to the root node of the graph. The function creates an empty hash map to keep track of nodes that have already
been cloned. Then it calls the clone_helper function, passing in the root node and the hash map.

![Solution 1](./images/solutions/clone_graph_solution_1.png)
![Solution 2](./images/solutions/clone_graph_solution_2.png)
![Solution 3](./images/solutions/clone_graph_solution_3.png)
![Solution 4](./images/solutions/clone_graph_solution_4.png)
![Solution 5](./images/solutions/clone_graph_solution_5.png)
![Solution 6](./images/solutions/clone_graph_solution_6.png)

### Time Complexity

The time complexity of this code is O(n+m), where n is the number of nodes, and m is the number of edges.

### Space Complexity

The space complexity of this code is O(n), where n is the number of nodes in the dictionary.

> Note: We can also solve this problem using BFS.


34 changes: 34 additions & 0 deletions datastructures/graphs/undirected/clone_graph/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Optional, Dict
from datastructures.graphs.undirected.clone_graph.node import Node


def clone(root: Optional[Node]) -> Optional[Node]:
def clone_helper(n: Optional[Node], nodes_cloned: Dict[Node, Node]) -> Optional[Node]:
# If the node is None, return None
if n is None:
return None

# Create a new Node with the same data as the node
cloned_node = Node(n.data)
# Add the node and its clone to the nodes_completed hash map
nodes_cloned[n] = cloned_node

# Iterate through the neighbours of the node
for neighbour in n.neighbors:
# Retrieve the value of key neighbour in nodes_completed hash map.
# If it exists, assign the corresponding cloned node to cloned_neighbour.
# This checks if the neighbour node neighbour has already been cloned.
cloned_neighbour = nodes_cloned.get(neighbour)
# If the neighbour is not cloned yet, recursively clone it
if cloned_neighbour is None:
cloned_node.neighbors += [clone_helper(neighbour, nodes_cloned)]
# If the neighbour is already cloned, add the cloned neighbour to the new
# node's neighbors
else:
cloned_node.neighbors += [cloned_neighbour]
return cloned_node

# Initialize an empty dictionary to keep track of cloned nodes
nodes_completed: Dict[Node, Node] = {}
# Call the recursive function to clone the graph starting from the root node
return clone_helper(root, nodes_completed)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions datastructures/graphs/undirected/clone_graph/node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from typing import Any, List


class Node:
def __init__(self, d: Any):
self.data: Any = d
self.neighbors: List["Node"] = []
Loading