Clone a Linked List
#include <bits/stdc++.h> // Common includesusing namespace std; // Standard namespace
// Node for a linked list with a random pointerclass Node {public: int data; // Node data Node *next, *random; // Pointers to next node and a random node
Node(int data) { // Constructor this->data = data; this->next = NULL; this->random = NULL; // Initialize random to NULL }};
// Print the linked list (data and random pointer's data)void printList(Node* head) { if(head == NULL) { // Empty list check cout << "List is Empty!" << endl; return; } while(head != NULL) { // Traverse and print // Prints [data, random_data] if random is not NULL, else handles NULL cout << "[" << head->data << ","; if (head->random != NULL) { cout << head->random->data; } else { cout << "NULL"; // Indicate if random points to NULL } cout << "] "; head = head->next; } cout << endl;}
// Insert new node at head (utility, not directly used in clone logic here)void insertAtHead(Node* &head, int data) { Node *temp = new Node(data); // Create new node temp->next = head; // Link new node to old head head = temp; // Update head}
// Insert new node at tail (utility, used to build clone list)void insertAtTail(Node* &head, Node* &tail, int data) { Node* temp = new Node(data); // Create new node
if(head == NULL) { // If list is empty, new node is head and tail head = temp; tail = temp; } else { // Append to tail tail->next = temp; // Current tail points to new node tail = temp; // Update tail to new node }}
// Function to clone a linked list with random pointers// Approach: Using a hash map (O(N) space)Node* cloneList(Node* head) { Node* cloneHead = NULL; // Head of the cloned list Node* cloneTail = NULL; // Tail of the cloned list Node* temp = head; // Iterator for original list map<Node*, Node*> mapping; // Map: original_node -> clone_node
// Step 1: Create a deep copy of nodes and populate mapping while(temp != NULL) { // Create new node with same data and append to clone list insertAtTail(cloneHead, cloneTail, temp->data); // Map original node to its new clone mapping[temp] = cloneTail; temp = temp->next; // Move to next original node }
// Step 2: Assign random pointers in the cloned list temp = head; // Reset iterator for original list Node* clone = cloneHead; // Iterator for clone list
while(clone != NULL) { // Iterate through the clone list // Use mapping to find the corresponding random pointer in clone list clone->random = mapping[temp->random]; temp = temp->next; // Advance original list iterator clone = clone->next; // Advance clone list iterator }
return cloneHead; // Return head of the cloned list}
int main() { // Manually create a sample linked list with random pointers Node *head = new Node(1); // Creating node 1 (head) Node *node2 = new Node(2); Node *node3 = new Node(3); Node *node4 = new Node(4); Node *node5 = new Node(5);
head->next = node2; node2->next = node3; node3->next = node4; node4->next = node5; node5->next = NULL; // End of list
// Set random pointers (example based on relative positions) // List: 1 -> 2 -> 3 -> 4 -> 5 // Indices:0 1 2 3 4 head->random = node3; // 1's random points to 3 (index 2) node2->random = head; // 2's random points to 1 (index 0) node3->random = node5; // 3's random points to 5 (index 4) node4->random = node3; // 4's random points to 3 (index 2) node5->random = node2; // 5's random points to 2 (index 1)
cout << "Original List : "; printList(head); // Print original list
Node *clone = cloneList(head); // Clone the list
cout << "Clone List : "; printList(clone); // Print the cloned list
return 0;}Quick Revision Notes: Cloning a Linked List with Random Pointers
Problem: Create a deep copy of a linked list where each node has a next pointer and an additional random pointer, which can point to any node in the list or NULL.
Challenge: Simple traversal and copying (next pointers) is easy. The difficulty is copying random pointers, as the nodes they point to might not have been created yet in the clone.
Approach Used (Hashing/Mapping - $O(N)$ Space):
-
Node Structure: Requires
data,next, andrandompointers. -
insertAtTail(Utility): Helps in efficiently building the new clone list by appending nodes. -
cloneList(Node* head)Function:-
Step 1: Create Nodes and Map Original to Clone.
- Goal: Create all new nodes for the clone list and establish a mapping between original nodes and their corresponding new clone nodes.
- Process:
- Initialize
cloneHead = NULL,cloneTail = NULL. - Use a
std::map<Node*, Node*> mapping;(orunordered_mapfor average $O(1)$ lookup). This map stores(original_node_address -> clone_node_address). - Traverse the
original list(usingtemp). - For each
tempnode:- Create a
new Node(temp->data). - Append this new node to the
clone listusinginsertAtTail. - Store the mapping:
mapping[temp] = clone_node_just_created.
- Create a
- Initialize
- Why? This step creates all the ‘normal’
nextlinks and provides a lookup table to resolverandompointers later. - Complexity: $O(N)$ time (for traversal and map insertions), $O(N)$ space (for
mapandclonenodes).
-
Step 2: Assign Random Pointers in Clone.
- Goal: Correctly set the
randompointers for all nodes in the cloned list. - Process:
- Reset
temp = head(for original list traversal). - Reset
clone = cloneHead(for clone list traversal). - Traverse both lists simultaneously.
- For each
clonenode:- Its
randompointer should point to the clone oftemp->random. - Use the
mappingto find this:clone->random = mapping[temp->random]; - This lookup handles
NULLgracefully iftemp->randomisNULL.
- Its
- Reset
- Why? Now that all original nodes are mapped to their clone counterparts, we can look up the correct clone target for each random pointer.
- Complexity: $O(N)$ time (for traversal and map lookups), $O(1)$ additional space.
- Goal: Correctly set the
-
Overall Complexity:
- Time: $O(N)$ (dominated by two passes over the list and map operations).
- Space: $O(N)$ (for the
mapand the new cloned list nodes).
Alternative (Optimized) Approach ($O(1)$ Space - for revision):
- Interweave Nodes: Create new clone nodes and insert them right after their corresponding original nodes.
1 -> 1' -> 2 -> 2' -> 3 -> 3' ...
- Assign Random Pointers: Traverse the interweaved list. For an original node
currand its clonecurr->next:curr->next->random = curr->random->next;(ifcurr->randomis notNULL).- This works because
curr->random->nextwill be the clone ofcurr->random.
- Separate Lists: Detach the original and clone lists by adjusting
nextpointers.
This $O(1)$ space method is often preferred in interviews but is more complex to implement correctly. The map-based approach is simpler and sufficient if $O(N)$ space is allowed.
#include <bits/stdc++.h> // Common includesusing namespace std; // Standard namespace
// Node for a linked list with a random pointerclass Node {public: int data; // Node data Node *next, *random; // Pointers to next node and a random node
Node(int data) { // Constructor this->data = data; this->next = NULL; this->random = NULL; // Initialize random to NULL }};
// Print the linked list (data and random pointer's data)void printList(Node* head) { if(head == NULL) { // Empty list check cout << "List is Empty!" << endl; return; } while(head != NULL) { // Traverse and print // Prints [data, random_data] if random is not NULL, else handles NULL cout << "[" << head->data << ","; if (head->random != NULL) { cout << head->random->data; } else { cout << "NULL"; // Indicate if random points to NULL } cout << "] "; head = head->next; } cout << endl;}
// Insert new node at head (utility, not directly used in clone logic here)void insertAtHead(Node* &head, int data) { Node *temp = new Node(data); // Create new node temp->next = head; // Link new node to old head head = temp; // Update head}
// Function to clone a linked list with random pointers// Optimized Approach: O(1) SpaceNode* cloneList(Node* head) { if (head == NULL) return NULL; // Handle empty list case
Node* temp = head; // Step 1: Create A' nodes and interweave with original A nodes: A -> A' -> B -> B' -> ... while(temp != NULL) { Node* next = temp->next; // Store original next Node* cloneNode = new Node(temp->data); // Create clone node temp->next = cloneNode; // Link original to clone cloneNode->next = next; // Link clone to original's next temp = next; // Move temp to the next original node }
// Step 2: Assign random pointers for A' nodes temp = head; // Reset temp to original head while(temp != NULL) { if (temp->random != NULL) { // A's random points to B' if A's random points to B temp->next->random = temp->random->next; } else { temp->next->random = NULL; // If original random is NULL, clone random is NULL } temp = temp->next->next; // Move temp to the next original node (skip clone) }
// Step 3: Detach original and cloned lists Node *cloneHead = head->next; // Head of the cloned list Node* clone = cloneHead; // Iterator for the clone list temp = head; // Iterator for the original list
while(clone->next != NULL) { // Loop until the second to last clone node temp->next = temp->next->next; // Original node points to its original next clone->next = clone->next->next; // Clone node points to its clone next temp = temp->next; // Advance original iterator clone = clone->next; // Advance clone iterator }
// Handle the last node's next pointer temp->next = NULL; // Last original node's next is NULL clone->next = NULL; // Last clone node's next is NULL
return cloneHead; // Return head of the cloned list}
int main() { // Manually create a sample linked list with random pointers Node *head = new Node(1); // Creating node 1 (head) Node *node2 = new Node(2); Node *node3 = new Node(3); Node *node4 = new Node(4); Node *node5 = new Node(5);
head->next = node2; node2->next = node3; node3->next = node4; node4->next = node5; node5->next = NULL; // End of list
// Set random pointers (example based on relative positions) // List: 1 -> 2 -> 3 -> 4 -> 5 // Indices:0 1 2 3 4 head->random = node3; // 1's random points to 3 node2->random = head; // 2's random points to 1 node3->random = node5; // 3's random points to 5 node4->random = node3; // 4's random points to 3 node5->random = node2; // 5's random points to 2
cout << "Original List : "; printList(head); // Print original list
Node *clone = cloneList(head); // Clone the list
cout << "Clone List : "; printList(clone); // Print the cloned list
// Verify original list is restored (optional) cout << "Original List after clone (should be restored): "; printList(head);
return 0;}Quick Revision Notes: Cloning a Linked List with Random Pointers (O(1) Space)
Problem: Create a deep copy of a linked list that includes both next and random pointers, without using extra space proportional to the list’s size.
Challenge: Directly copying random pointers is hard as target nodes in the clone list may not exist yet.
Approach Used (Interweaving Nodes - $O(1)$ Space):
-
Node Structure:
data,next,randompointers. -
cloneList(Node* head)Function:-
Step 1: Interweave Cloned Nodes.
- Goal: Create a copy of each original node and insert it immediately after its original counterpart.
- Process:
- Traverse the original list.
- For each
original_node:- Create
clone_nodewithoriginal_node->data. - Set
clone_node->next = original_node->next. - Set
original_node->next = clone_node. - Move to
original_node->next->next(the next original node).
- Create
- Resulting Structure:
Original1 -> Clone1 -> Original2 -> Clone2 -> Original3 -> Clone3 ... - Complexity: $O(N)$ time, $O(1)$ extra space (besides new nodes).
-
Step 2: Assign Random Pointers for Cloned Nodes.
- Goal: Set the
randompointers for all theclone_nodes. - Process:
- Traverse the interweaved list (again starting from
head). - For each
original_node(curr):- Its clone is
original_node->next. - If
original_node->randomis notNULL:clone_node->random = original_node->random->next;(The clone’s random points to the clone of what the original’s random pointed to).
- If
original_node->randomisNULL:clone_node->random = NULL;
- Its clone is
- Move to the next original node:
curr = curr->next->next.
- Traverse the interweaved list (again starting from
- Complexity: $O(N)$ time, $O(1)$ extra space.
- Goal: Set the
-
Step 3: Detach Original and Cloned Lists.
- Goal: Separate the two interwoven lists into two distinct lists.
- Process:
- Initialize
cloneHead = head->next. - Use two pointers:
original_curr(starts athead) andclone_curr(starts atcloneHead). - Loop until
clone_curr->nextisNULL(i.e., until the second-to-last node in the clone list).original_curr->next = original_curr->next->next;(Reconnect original list).clone_curr->next = clone_curr->next->next;(Reconnect clone list).- Advance both pointers.
- Handle the last node’s
nextpointers (set toNULL). original_curr->next = NULL;clone_curr->next = NULL;
- Initialize
- Complexity: $O(N)$ time, $O(1)$ extra space.
-
Overall Complexity:
- Time: $O(N)$ (three passes over the list).
- Space: $O(1)$ (no auxiliary data structure like a map is used; memory for clone nodes is created directly).
Example Trace (List: 1 -> 2 -> 3, 1.random->3, 2.random->1, 3.random->2):
-
Interweave:
1 -> 1' -> 2 -> 2' -> 3 -> 3' -> NULL -
Assign Randoms:
1'.random =1.random->next (3->next) =3'2'.random =2.random->next (1->next) =1'3'.random =3.random->next (2->next) =2'
-
Separate:
- Original:
1 -> 2 -> 3 -> NULL(restored) - Clone:
1' -> 2' -> 3' -> NULL(new clone list) - Randoms are correctly set within the clone list.
- Original:
This method is more complex to implement but highly space-efficient.