
Problem Statement
In this task, we have a unique data structure called a linked list where each node not only points to the next node in sequence via the next pointer, but also has an additional random pointer. This random pointer can link to any node within the list (or to none at all, i.e., null).
The challenge stems from needing to create a deep copy of this linked list. A deep copy involves generating entirely new nodes that replicate the value and the pattern of next and random connections of the original list. Unlike a shallow copy, where the copy might still contain references to original pieces, each node and linkage in a deep copy stands alone and independent of the original dataset.
This problem calls for precision in handling pointers, particularly ensuring that the new list's random pointers correlate directly with the new corresponding nodes, not the initial ones. Therefore, the outcome should maintain both the sequence (next pointers) and the random relationships, without retaining any references to the original list nodes.
Examples
Example 1
Input:
head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output:
[[7,null],[13,0],[11,4],[10,2],[1,0]]
Example 2
Input:
head = [[1,1],[2,1]]
Output:
[[1,1],[2,1]]
Example 3
Input:
head = [[3,null],[3,0],[3,null]]
Output:
[[3,null],[3,0],[3,null]]
Constraints
0 <= n <= 1000-104 <= Node.val <= 104Node.randomisnullor is pointing to some node in the linked list.
Approach and Intuition
To tackle the problem of creating a deep copy of a linked list with both next and random pointers, consider the following approach, drawn from the provided examples and constraints:
Iterative Relation Establishment with Mapping:
- Initialize a hashmap to track and relate original nodes to their respective clones. This data structure will expedite access and prevent the recreation of already copied nodes.
- Traverse the original linked list. For each node, create a clone and add it to the hashmap.
- On this first pass, only establish the
nextpointers for the clones using the hashmap to prevent the lingering dependency on original nodes. - In a subsequent traversal, set up the
randompointers for each cloned node. Use the hashmap to translate the random pointers from the original to the corresponding cloned nodes accurately.
Edge Cases Handling:
- For lists of length
n = 0, simply returnnullas there are no nodes to copy. - For the random pointer, ensure each cloned
randompointer maps correctly even if the original pointer isnull.
- For lists of length
Efficiency Considerations:
- Given the constraint (
0 <= n <= 1000), creating a structure of one hashmap and two full list traversals should remain computationally reasonable. - Directly read and establish connections in two separate passes to avoid complications or corruptions, such as prematurely linking clones or inadvertently contiguous assignments.
- Given the constraint (
This approach, by accurately using an auxiliary hashmap and ensuring comprehensiveness in node linkage assignment, encapsulates an efficient and error-resistant method to achieve the deep copy of a complex linked list.
Solutions
- C++
- Java
- C
- JavaScript
- Python
class Solution {
public:
Node* cloneListWithRandomPointer(Node* original_head) {
if (!original_head) {
return nullptr;
}
Node* current = original_head;
while (current != nullptr) {
Node* clone = new Node(current->val, nullptr, nullptr);
clone->next = current->next;
current->next = clone;
current = clone->next;
}
current = original_head;
while (current != nullptr) {
current->next->random = current->random ? current->random->next : nullptr;
current = current->next->next;
}
Node* old_list = original_head;
Node* new_list = original_head->next;
Node* new_head = original_head->next;
while (old_list != nullptr) {
old_list->next = old_list->next->next;
new_list->next = new_list->next ? new_list->next->next : nullptr;
old_list = old_list->next;
new_list = new_list->next;
}
return new_head;
}
};
This solution outlines how to clone a linked list where each node contains an additional pointer to a random node in the list. The function cloneListWithRandomPointer implements this operation in C++. Here’s a breakdown of the method used:
- Firstly, traverse the original linked list to create a new node for each existing node. Insert these new nodes directly after their corresponding original nodes.
- Next, assign the
randompointer of each new node. This is done by setting it to therandompointer of the original node's next node, taking care to check if the original node'srandompointer is null. - Finally, separate the interleaved list of original and new nodes. This involves correcting the
nextpointers of both the original and the copied nodes to restore the original list and to finish forming the separate cloned list.
The outcome is a new linked list where each node from the original list is duplicated with both the next and random pointers correctly assigned, effectively deep copying the initial list with its random pointer structure intact.
Ensure following this explanation allows for an understanding of manipulating complex data structures, such as linked lists with additional pointer tracking.
public class Solution {
public Node cloneRandomLinkedList(Node originalHead) {
if (originalHead == null) {
return null;
}
Node current = originalHead;
while (current != null) {
Node clonedNode = new Node(current.val);
clonedNode.next = current.next;
current.next = clonedNode;
current = clonedNode.next;
}
current = originalHead;
while (current != null) {
current.next.random = (current.random != null) ? current.random.next : null;
current = current.next.next;
}
Node originalCurrent = originalHead;
Node clonedCurrent = originalHead.next;
Node clonedHead = originalHead.next;
while (originalCurrent != null) {
originalCurrent.next = originalCurrent.next.next;
clonedCurrent.next = (clonedCurrent.next != null)
? clonedCurrent.next.next
: null;
originalCurrent = originalCurrent.next;
clonedCurrent = clonedCurrent.next;
}
return clonedHead;
}
}
The provided Java code defines a method cloneRandomLinkedList within the Solution class, which is designed to clone a complex linked list where each node has a next pointer and a random pointer. The task involves deep copying a list such that the new list is a standalone copy of the original list with the same val, next, and random connections.
Below is the step-by-step approach taken in the code to accomplish this:
- The method first checks if the input head node,
originalHead, is null. If it is, it returns null indicating there are no nodes to duplicate. - Define
currentto traverse the original list. Iterate through the list:- Create a new node,
clonedNode, for each original node mirroring theval. - Insert the cloned node just after the original node by adjusting the
nextpointers, effectively weaving the cloned nodes among the original nodes.
- Create a new node,
- Set
currentback tooriginalHeadto start the second pass through the list:- Assign the
randompointer of each cloned node. This is achieved by pointing it to thenextof therandompointed node of the original, if therandompointer is not null.
- Assign the
- Initialize pointers to handle separation of the original and cloned list:
originalCurrentpoints to the beginning of the intertwined list.clonedCurrentandclonedHeadpoint to the head of the cloned list.
- Decouple the original and cloned lists:
- Advance
originalCurrentandclonedCurrentthrough their respective lists resetting thenextpointers to restore the original list and to finalize the separate cloned list.
- Advance
This method uses space efficiently by avoiding the use of extra space for a map typically used to keep track of cloned nodes vis-a-vis their corresponding original nodes. The node intertwining and later separation technique ensures both lists are properly set up with correct pointers, all within a linear time complexity relative to the number of nodes in the original list.
struct Node* createNode(int value, struct Node* nextNode, struct Node* randomPointer) {
struct Node* tempNode = (struct Node*)malloc(sizeof(struct Node));
tempNode->val = value;
tempNode->next = nextNode;
tempNode->random = randomPointer;
return tempNode;
}
struct Node* cloneLinkedListWithRandomPointer(struct Node* originalHead) {
if (!originalHead) {
return NULL;
}
struct Node* traverse = originalHead;
while (traverse) {
struct Node* clonedNode = createNode(traverse->val, NULL, NULL);
clonedNode->next = traverse->next;
traverse->next = clonedNode;
traverse = clonedNode->next;
}
traverse = originalHead;
while (traverse) {
traverse->next->random = (traverse->random) ? traverse->random->next : NULL;
traverse = traverse->next->next;
}
struct Node* oldListPointer = originalHead;
struct Node* newListPointer = originalHead->next;
struct Node* clonedHead = originalHead->next;
while (oldListPointer) {
oldListPointer->next = oldListPointer->next->next;
newListPointer->next = (newListPointer->next) ? newListPointer->next->next : NULL;
oldListPointer = oldListPointer->next;
newListPointer = newListPointer->next;
}
return clonedHead;
}
The provided C code tackles the problem of duplicating a linked list where each node possesses an additional random pointer apart from the standard next pointer. The solution involves creating a deep copy of the list, ensuring that the random links between nodes are accurately reproduced in the new list.
The first function,
createNode, is a helper function to initialize and allocate memory for a new node when given a value, a pointer to the next node, and a pointer to a random node.The main function,
cloneLinkedListWithRandomPointer, begins by handling the edge case where the input list is empty, returningNULLif so.Next, it loops through the original list, inserting a cloned node immediately after each original node. This is achieved by:
- Allocating a new node for each original node using
createNode. - Adjusting
nextpointers to include the newly created clone node right after the original one.
- Allocating a new node for each original node using
Following the clone insertion, the code proceeds to assign the
randompointers for each of the cloned nodes. It does this in a second traversal of the list, ensuring each cloned node'srandompoints to the subsequent node of its counterpart'srandompointer if it exists; otherwise, it is set toNULL.Finally, the function extracts the cloned nodes to form a new list. It adjusts the
nextpointers of both original and cloned nodes:- Setting the original nodes'
nextto skip over their respective clones. - Connecting the cloned nodes to each other correctly based on the original sequence.
- Setting the original nodes'
The sequence of links adjustments ensures that the original list remains intact, and a new list that mirrors the structure and random connections of the original is returned.
The approach carefully handles memory management and pointer adjustments to create a cloned list, which exactly replicates the complicated connection schemas of the original.
var duplicateRandomList = function (origin) {
if (origin === null) {
return null;
}
let current = origin;
while (current !== null) {
let copied = new Node(current.val, null, null);
copied.next = current.next;
current.next = copied;
current = copied.next;
}
current = origin;
while (current !== null) {
current.next.random = current.random !== null ? current.random.next : null;
current = current.next.next;
}
let oldList = origin; // Original nodes list
let newList = origin.next; // Copied nodes list
let savedHead = origin.next;
while (oldList !== null) {
oldList.next = oldList.next.next;
newList.next = newList.next !== null ? newList.next.next : null;
oldList = oldList.next;
newList = newList.next;
}
return savedHead;
};
This JavaScript solution addresses the problem of duplicating a linked list where each node has a next pointer and a random pointer that can point to any node in the list or null.
Here's a breakdown of how the solution works:
First, check if the input list,
origin, isnull. If it is, returnnullsince there is no list to copy.Iterate through the original list and for each node, create a new node with the same value. Insert these new nodes right after their corresponding original nodes. This interleaving of old and new nodes helps keep track of the random pointers in the subsequent steps.
Once the new nodes are inserted, iterate through the list again to update the
randompointers of the new nodes. For each original node'srandompointer, point the corresponding new node'srandomto the new node of the original random's target. This step effectively clones the random linkage between nodes.Separate the interleaved list into the original list and the copied list. Initialize two pointers,
oldListfor traversing the original nodes, andnewListfor traversing the new nodes. Adjust thenextpointers for both lists to extract the copied list while restoring the original list's structure.Finally, return the head of the new list,
savedHead, which is a fully duplicated list mirroring the original list with bothnextandrandompointers correctly set.
This approach efficiently clones the list in linear time complexity with constant space complexity, making it a robust solution for duplicating linked lists with complex internal linkages.
class Solution:
def duplicateRandomList(self, head: "Optional[Node]") -> "Optional[Node]":
if not head:
return head
# Creating intertwining of old and duplicated nodes
current = head
while current:
# Create duplicate node
duplicate = Node(current.val, None, None)
# Place the duplicate node beside the original
duplicate.next = current.next
current.next = duplicate
current = duplicate.next
current = head
# Set the 'random' pointer for the duplicate nodes
while current:
current.next.random = current.random.next if current.random else None
current = current.next.next
# Decouple the two interleaved linked lists
old_ptr = head # Original nodes path
dup_ptr = head.next # Duplicated nodes path
dup_head = head.next
while old_ptr:
old_ptr.next = old_ptr.next.next
dup_ptr.next = dup_ptr.next.next if dup_ptr.next else None
old_ptr = old_ptr.next
dup_ptr = dup_ptr.next
return dup_head
This Python function efficiently duplicates a linked list where each node contains an additional random pointer that might point to any node within the list or be null. The strategy implemented here involves three main steps:
Interweave the original list with cloned nodes:
- Traverse the original list and create a new node for each existing node.
- Insert each new node directly after its corresponding original node, modifying the
nextpointers accordingly.
Establish correct
randompointers for the cloned nodes:- Again traverse the list. For each original node's cloned node, set the
randompointer by using the original node'srandompointer. - The key is to assign the cloned
randompointer to point to the next node of the originalrandompointer. This handles cases whether the originalrandompointer isnull.
- Again traverse the list. For each original node's cloned node, set the
Separating the intertwined lists into two independent lists:
- Initialize two pointers, one for the original list and one for the clone, then iterate through the intertwined list, appropriately adjusting the
nextpointers to decouple the original nodes from their clones.
- Initialize two pointers, one for the original list and one for the clone, then iterate through the intertwined list, appropriately adjusting the
During this entire procedure, the function ensures that the structural integrity of the original list is maintained, and each cloned node is correctly linked, both through the next and random pointers. The function returns the head of the copied list, which facilitates further operations on the duplicated structure without affecting the original list. This in-place solution adds efficiency in terms of space, working without the need for additional data structures for mapping old nodes to their duplicates.