diff --git a/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/program.py b/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/program.py new file mode 100644 index 0000000000..e0dc2a80dd --- /dev/null +++ b/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/program.py @@ -0,0 +1,74 @@ +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + self.random = None + +# Function to clone the linked list with random pointers +def clone_linked_list_with_random_pointer(head): + if not head: + return None + + # Step 1: Create a dictionary to store the mapping from original to cloned nodes + node_map = {} + + # Step 2: First pass to create all nodes (without setting next or random) + curr = head + while curr: + node_map[curr] = ListNode(curr.val) # Map original node to its clone + curr = curr.next + + # Step 3: Second pass to set next and random pointers + curr = head + while curr: + node_map[curr].next = node_map.get(curr.next) # Set the next pointer + node_map[curr].random = node_map.get(curr.random) # Set the random pointer + curr = curr.next + + # Return the head of the cloned list + return node_map[head] + +# Helper function to create a linked list from a list of values and random indices +def create_linked_list(values, random_indices): + if not values: + return None + + # Create all nodes + nodes = [ListNode(val) for val in values] + + # Set the next pointers + for i in range(len(nodes) - 1): + nodes[i].next = nodes[i + 1] + + # Set the random pointers based on random_indices + for i, random_index in enumerate(random_indices): + if random_index != -1: # -1 indicates no random pointer + nodes[i].random = nodes[random_index] + + return nodes[0] + +# Helper function to print the linked list along with random pointers +def print_linked_list(head): + curr = head + while curr: + random_val = curr.random.val if curr.random else "NULL" + print(f"Node({curr.val}) -> Random({random_val})") + curr = curr.next + +# Main function to demonstrate cloning +if __name__ == "__main__": + # Define values and random indices for each node + values = [1, 2, 3, 4, 5] + random_indices = [2, 4, -1, 0, 1] # -1 indicates no random pointer + + # Create the linked list with random pointers + original_list = create_linked_list(values, random_indices) + + print("Original list with random pointers:") + print_linked_list(original_list) + + # Clone the linked list + cloned_list = clone_linked_list_with_random_pointer(original_list) + + print("\nCloned list with random pointers:") + print_linked_list(cloned_list) diff --git a/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/readme.md b/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/readme.md new file mode 100644 index 0000000000..55aa41f902 --- /dev/null +++ b/Algorithms_and_Data_Structures/Linked List/Clone_a_List_with_Random_Pointers/readme.md @@ -0,0 +1,57 @@ +# Clone a Linked List with Random Pointers + +## Problem Statement + +Given a linked list where each node contains: +- An integer `val`. +- A `next` pointer to the next node in the list. +- A `random` pointer that points to any node in the list or is `NULL`. + +The task is to **create a deep copy (clone) of this linked list**. The cloned list should have the same values, `next`, and `random` pointer connections as the original list but should not share any nodes with it. + +## Example + +Consider a linked list where: +- Node values: `[1, 2, 3, 4, 5]` +- Random pointers map as follows: + - `1`'s random points to `3` + - `2`'s random points to `5` + - `3` has no random pointer (`NULL`) + - `4`'s random points to `1` + - `5`'s random points to `2` + +The structure of both the original and cloned list should look like this: + +Original list: 1 -> 2 -> 3 -> 4 -> 5 | | | ↓ ↓ ↓ 3 5 1 + +Cloned list: 1' -> 2' -> 3' -> 4' -> 5' | | | ↓ ↓ ↓ 3' 5' 1' + +## Solution Approach + +1. **Mapping Original Nodes to Clones**: Use a dictionary to map each node in the original list to its corresponding cloned node, created based on its `val`. +2. **Setting Pointers**: Traverse the list a second time to set the `next` and `random` pointers for each cloned node. +3. **Return Cloned List**: Return the head of the cloned list. + +## Steps to Solve + +### Step 1: Mapping Original Nodes to Clones +- Use a dictionary (`node_map`) to store a mapping of each node in the original list to its clone, initially creating cloned nodes without setting `next` or `random` pointers. + +### Step 2: Setting `next` and `random` Pointers +- Traverse the list again and set the `next` and `random` pointers for each cloned node using the `node_map` dictionary: + - **Next Pointer**: Map each node's `next` pointer to the corresponding clone. + - **Random Pointer**: Map each node's `random` pointer to the corresponding clone. + +### Step 3: Return the Cloned Head +- Return the cloned head node from `node_map`. + +## Code Walkthrough + +1. **`clone_linked_list_with_random_pointer(head)`**: Clones the linked list using a dictionary to map each original node to its clone. +2. **`create_linked_list(values, random_indices)`**: Creates a linked list with given `values` and `random` pointers based on the indices in `random_indices`. +3. **`print_linked_list(head)`**: Helper function to print each node’s value and its `random` pointer, if any. + + +**Explanation of Complexity** +- Time Complexity: 𝑂(𝑛), where n is the number of nodes in the list. We traverse the list twice. +- Space Complexity: 𝑂(𝑛), due to the use of the hash map to store mappings from original to cloned nodes.