Start learning free

LESSON 279 OF 338 · LINKED LISTS

Copy List with Random Pointer in Go: O(n) Time, O(1) Space

Practice O(n) Read the full explanation

A linked list of length n is given such that each node contains an additional random pointer, which could point to any node in the list, or null.

Construct a deep copy of the list. The deep copy should consist of exactly n brand new nodes, where each new node has its value set to the value of its corresponding original node. Both the next and random pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. None of the pointers in the new list should point to nodes in the original list.

For example, if there are two nodes X and Y in the original list, where X.random --> Y, then for the corresponding two nodes x and y in the copied list, x.random --> y.

Return the head of the copied linked list.

The linked list is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

  • val: an integer representing Node.val
  • random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or null if it does not point to any node.

Your code will only be given the head of the original linked list.

 

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

Example 4:

Input: head = []
Output: []
Explanation: The given linked list is empty (null pointer), so return null.

 

Constraints:

  • 0 <= n <= 1000
  • -10000 <= Node.val <= 10000
  • Node.random is null or is pointing to some node in the linked list.

Note:

Your algorithm should run in O(n) time and use O(1) extra space.


STEP 1 OF 18
    Ctrl + Enter to check

    GO · LINKED LISTS

    Copy List Random Pointer in Go, explained

    Problem overview

    The function copyRandomList receives the head of a linked list of *ListNode values. Each node has Val, Next and a Random pointer that may point to any node of the list or be nil. The function returns the head of a deep copy: n new nodes with the same values, whose Next and Random pointers point only to other new nodes. The original list must keep its structure.

    For the list [[7,null],[13,0],[11,4],[10,2],[1,0]], written as [val, random_index] pairs, the copy has the same five pairs, but it consists of new nodes. An empty list gives nil. This version must run in O(n) time and use O(1) extra space, which rules out a map from originals to copies.

    Approach: weave each copy in after its original

    The map-based solution answers the question "which node is the copy of X?" with a lookup table. The constant-space solution stores the answer in the list itself. It inserts each copy directly after its original, so the copy of X is always X.Next:

    A -> B -> C           becomes           A -> A' -> B -> B' -> C -> C'

    Then the whole job takes three passes:

    1. Interleave. After each original node, insert a copy that holds the same value.
    2. Set the random pointers. If an original node has node.Random, then its copy is node.Next, and its random target's copy is node.Random.Next. So node.Next.Random = node.Random.Next.
    3. Separate. Unweave the two lists: restore each original's Next, and chain the copies to one another. The input is restored exactly as it was.

    The extra memory is just a few pointers, because the n new nodes are the output.

    Step by step

    1. Handle the empty list. If head == nil, return nil. The code later reads head.Next.
    2. Start the first loop with for node := head; node != nil; {. The post statement is empty, since the body moves node itself.
    3. Save next := node.Next. The next step overwrites that link.
    4. Build the copy with its value and its Next already set: copy := &ListNode{Val: node.Val, Next: next}. It slots into the chain in one step, and its Random starts as nil.
    5. Splice it in: node.Next = copy.
    6. Jump over the copy: node = next. Moving to node.Next would land on the copy and insert copies of copies forever.
    7. Start the second loop over the originals. The post statement node = node.Next.Next hops two nodes, from original to original. This is safe because every original is followed by its copy.
    8. Check node.Random != nil. Copies of nodes without a random target already have a nil one.
    9. Set the copy's random: node.Next.Random = node.Random.Next. The node after the target is the target's copy.
    10. Save the answer before the lists are separated: newHead := head.Next, the first copy.
    11. Start the third loop, again over the originals with an empty post statement.
    12. Take the copy of the current node: copy := node.Next.
    13. Save the next original, next := copy.Next, before any link is rewired.
    14. Restore the original list: node.Next = next.
    15. Check next != nil. The last copy has no following copy, and its Next is already nil.
    16. Chain the copies: copy.Next = next.Next. The node after next is next's copy.
    17. Advance with node = next.
    18. Return newHead.

    The solution in Go

    func copyRandomList(head *ListNode) *ListNode {
    	if head == nil {
    		return nil
    	}
    	for node := head; node != nil; {
    		next := node.Next
    		copy := &ListNode{Val: node.Val, Next: next}
    		node.Next = copy
    		node = next
    	}
    	for node := head; node != nil; node = node.Next.Next {
    		if node.Random != nil {
    			node.Next.Random = node.Random.Next
    		}
    	}
    	newHead := head.Next
    	for node := head; node != nil; {
    		copy := node.Next
    		next := copy.Next
    		node.Next = next
    		if next != nil {
    			copy.Next = next.Next
    		}
    		node = next
    	}
    	return newHead
    }
    

    Go details worth knowing:

    • Loop variables belong to the loop. node is declared in each for statement's init part and exists only inside that loop, so each of the three loops declares its own.
    • The init-only for. for node := head; node != nil; { has an empty post statement. That is legal, and the body advances node itself, which the first and third loops need because they don't step to node.Next.
    • A variable named copy. Go has a built-in function copy for slices. Declaring a variable with that name shadows it inside the loop body. That is harmless here, since the built-in isn't used, but it's a name to avoid in code that also copies slices.
    • Composite literal with several fields. &ListNode{Val: node.Val, Next: next} sets two fields by name and leaves Random as nil.

    Complexity

    Time: O(n). Each of the three loops visits every original node once, and the work per node is constant.

    Space: O(1) extra. There is no map and no slice. The only memory beyond the n required copies is a handful of pointer variables. The interleaving temporarily doubles the length of the original list, and the third pass undoes it.

    Edge cases and common mistakes

    • Empty list. The guard at the top returns nil. Without it, head.Next would panic.
    • Doing the passes in the wrong order. The random pointers must be set before the lists are separated. After separation, node.Random.Next no longer leads to a copy.
    • Not restoring the original. The caller still owns the input list. If the third pass is skipped or left half done, the original stays interleaved with the copies.
    • Reading node.Random.Next when Random is nil. The check on node.Random prevents a nil pointer dereference.
    • Losing the last copy's end. For the last original, next is nil, so next.Next must not be read. The check protects this, and the copy's Next is nil already.
    • Advancing to node.Next in the first loop. That visits the copy just inserted and makes copies of copies without end.
    • Setting a random pointer to the original's target. node.Next.Random = node.Random would leave the copy pointing into the original list.

    Testing

    • [[7,null],[13,0],[11,4],[10,2],[1,0]] gives the same five pairs, on new nodes. The original list still has its five nodes in order afterwards.
    • [[1,1],[2,1]] gives [[1,1],[2,1]].
    • [[3,null],[3,0],[3,null]] gives [[3,null],[3,0],[3,null]].
    • [] gives [].
    Keep going with GoAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free