Problem overview
A linked list node has a val, a next link and a random link that points to any node of the list or to nothing. A Link is Option<Rc<RefCell<ListNode>>>. The function copy_random_list receives the head and must return the head of a deep copy: n brand-new nodes with the same values, where every next and random link of the copy points inside the copy.
The input [[7,null],[13,0],[11,4],[10,2],[1,0]] lists each node as its value and the index of its random target. The copy must have the same shape, with the new 13 pointing to the new 7. An empty list gives None. This version must run in O(n) time and use O(1) extra space, so a map from originals to copies is not allowed.
Approach: weave each copy next to its original
The map in the other approach exists to answer one question: where is the copy of node X? The idea here is to make the answer a pointer you can follow. Put each copy directly after its original, so the list reads A, A', B, B', C, C'. Then the copy of any node X is simply X.next.
That gives three passes over the list:
- Interleave. After each original node, insert a new node with the same value.
- Set the random links. If an original
AhasA.random = C, thenA'.randommust beC', which isC.next. SoA'.random = A.random.next. This works because every copy exists before this pass starts. - Separate. Unweave the two lists: rewire the originals to skip their copies, and the copies to skip the originals. This also restores the input list, which the caller still owns.
Try it on a two-node list where the first node's random points to the second. After pass 1 the chain is A, A', B, B'. In pass 2, A.random is B, and B's next is B', so A'.random becomes B'. Pass 3 leaves A, B as before and A', B' as a separate list, with the random link of A' inside it.
Step by step
- Guard the empty list. If
head.is_none(), returnNone. Later the code reads the first node'snext, which would fail on an empty list. - Create the cursor
nodeashead.clone(). Cloning anRccopies the pointer, not the node. - First loop with
while let Some(curr) = node, to insert a copy after every original. - Save the successor. Put
curr.borrow().next.clone()innext_node. Inserting the copy overwritescurr's next link, and without the saved link the rest of the list would be lost. - Create the copy,
copy_node, fromcurr.borrow().val. Its random link staysNonefor now. - Splice it in by setting
curr.borrow_mut().nexttoSome(copy_node.clone()). - Chain it onward by setting the copy's
nextto the savednext_node. - Advance to
next_node, not tocurr's next, which is now the copy. This skips the copy just inserted. - Restart the cursor with
node = head.clone(). - Second loop, to set the random links. Here
curronly visits originals. - Find the copy:
curr.borrow().next.clone().unwrap(). After pass 1 it is always there. - Copy the random link when
currhas one: set the copy's random torandom.borrow().next.clone(), the node after the target, which is the target's copy. - Jump to the copy's next, the next original.
- Save the new head as the node after
head, read withhead.as_ref().unwrap().borrow().next.clone(). Do it before the third pass changeshead's next. - Restart the cursor once more.
- Third loop, to separate the lists. In each pass take the copy after
curr, then save the next original from the copy's next before anything is rewired. - Link the copies together. If there is a next original, set the copy's next to the copy that follows it.
- Restore the original. Set
curr's next to the saved next original, then advance to it. - Return
new_head.
The solution in Rust
// The list node, already defined in the program:
// struct ListNode {
// val: i32,
// next: Link,
// random: Link,
// }
// type Link = Option<Rc<RefCell<ListNode>>>;
// ListNode::new(val) makes a new node, an Rc<RefCell<ListNode>>, whose next and random are None.
impl Solution {
fn copy_random_list(head: Link) -> Link {
if head.is_none() {
return None;
}
let mut node = head.clone();
while let Some(curr) = node {
let next_node = curr.borrow().next.clone();
let copy_node = ListNode::new(curr.borrow().val);
curr.borrow_mut().next = Some(copy_node.clone());
copy_node.borrow_mut().next = next_node.clone();
node = next_node;
}
node = head.clone();
while let Some(curr) = node {
let copy_node = curr.borrow().next.clone().unwrap();
if let Some(random) = curr.borrow().random.clone() {
copy_node.borrow_mut().random = random.borrow().next.clone();
}
node = copy_node.borrow().next.clone();
}
let new_head = head.as_ref().unwrap().borrow().next.clone();
node = head.clone();
while let Some(curr) = node {
let copy_node = curr.borrow().next.clone().unwrap();
let next_node = copy_node.borrow().next.clone();
if let Some(next) = &next_node {
copy_node.borrow_mut().next = next.borrow().next.clone();
}
curr.borrow_mut().next = next_node.clone();
node = next_node;
}
new_head
}
}
Rust details worth noticing:
- Shared, mutable nodes.
Rclets several links own the same node, andRefCelllets you change it through a shared handle:borrow()to read,borrow_mut()to write. The borrow rules are checked while the program runs. - Short borrows. Most
borrow()calls here are temporaries that end with their statement. That is whycurr.borrow_mut().next = Some(copy_node.clone());is safe right aftercurr.borrow().next.clone(): the earlier read borrow is already gone. Each statement that holds two borrows holds them on different nodes, one to read and one to write. if letand borrows. Inif let Some(random) = curr.borrow().random.clone(), the temporary read borrow ofcurrlasts through the whole body. The body only writes tocopy_nodeand readsrandom, so it never needs a write borrow ofcurr, and there is no conflict.&next_node.if let Some(next) = &next_nodematches on a reference, sonext_nodeisn't moved and can be used after theif.- Reassigning
node.while let Some(curr) = nodemoves the value out ofnode, so each pass must assign it a new value, and the code does so on its last line. In the third loop,node = next_nodemovesnext_node, which is why the earlier uses clone it. unwrap()calls. Each one is on a node that exists because pass 1 inserted a copy after every original.
Complexity
Time: O(n). Three passes, each visiting every node once with constant work per node.
Space: O(1) extra. Apart from the n copies that make up the output, the code keeps only a few Rc handles. No map, no vector and no recursion.
Edge cases and common mistakes
- An empty list. The guard returns
Nonebefore any loop runs. - A single node. The chain becomes A, A'. Pass 3 sees no next original, leaves the copy's next as
Noneand sets A's next back toNone. - Random pointing to itself. For
[[5,0]],A.randomis A and A's next is A', soA'.randombecomes A'. - Advancing to
curr's next in pass 1. That is the copy, so the loop would keep copying copies and never end. - Setting random links in pass 1. The target's copy may not exist yet. Do it in a pass of its own.
- Reading the new head too late. After pass 3,
head's next is the original second node, not the copy. - Not restoring the original. A copy that leaves the input list interleaved is wrong, since the caller still owns that list.
- Rewiring before saving. In pass 3, read the next original from the copy before you change the copy's next.
Testing
[[7,null],[13,0],[11,4],[10,2],[1,0]]gives the same list in new nodes, and the input is unchanged.[[1,1],[2,1]]gives[[1,1],[2,1]].[[3,null],[3,0],[3,null]]gives[[3,null],[3,0],[3,null]].[[5,0]]gives[[5,0]].[]gives[].

