Start learning free

LESSON 310 OF 415 · LINKED LISTS

Copy List with Random Pointer in Swift: O(1) Space by Interleaving

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 nil.

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, the value of the node
  • random_index: the index of the node (range from 0 to n-1) that the random pointer points to, or nil 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 (nil), so return nil.

 

Constraints:

  • 0 <= n <= 1000
  • -10000 <= val <= 10000
  • random is nil 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 22
    Ctrl + Enter to check

    SWIFT · LINKED LISTS

    Copy List Random Pointer in Swift, explained

    Problem overview

    The method copyRandomList receives the head of a singly linked list whose ListNodes have a val, a next link and a random link. The random link points to any node of the list or is nil. The method must return a deep copy: n brand-new nodes with the same values, whose next and random links point only to other new nodes, in the same pattern as the original. For an empty list it returns nil.

    Here the statement asks for O(n) time and O(1) extra space, so a dictionary from originals to copies, which would take O(n) space, is not allowed. The original list must also be the same when the method ends, since the caller still owns it.

    In the first example, the nodes written as [val, random_index] are [7,null], [13,0], [11,4], [10,2] and [1,0], and the copy has the same values and the same random positions.

    Approach: let each copy sit next to its original

    A dictionary answers one question: "which node is the copy of this one?" The lesson answers it without extra memory by putting each copy directly after its original in the list. Then the copy of any node X is simply X.next. The method makes three passes:

    1. Interleave. Turn A → B → C into A → A' → B → B' → C → C', where each primed node is a copy that holds only the value.
    2. Set the random links. If an original X has X.random = Y, then the copy X', which is X.next, gets Y.next, which is Y'. No map is needed.
    3. Split. Separate the two lists again: restore every original's next and link the copies to each other.

    The interleaved list is temporary. It uses the original nodes' next fields as scaffolding, and the third pass undoes it.

    Step by step

    1. Guard the empty list. If head is nil, return nil. Later code reads head!.next, which would crash without the guard.
    2. Start the first pass with var node = head and loop while node != nil.
    3. Save the original successor in let nextNode = node!.next. Inserting the copy overwrites node's next, and the rest of the list would be unreachable without this saved reference.
    4. Create the copy with ListNode(node!.val). Only the value is copied now.
    5. Splice it in: node!.next = copyNode and then copyNode.next = nextNode.
    6. Move to nextNode, not to node!.next. Moving to the copy would insert copies of copies forever.
    7. Second pass. Reset node = head and loop again. Each iteration reads the copy as node!.next.
    8. Copy the random link. If node!.random is not nil, set copyNode!.random = node!.random!.next, which is the copy of the target.
    9. Jump over the copy: node = copyNode!.next, which is the next original, or nil.
    10. Remember the answer. let newHead = head!.next is the first copy. It has to be saved before the third pass changes head's link.
    11. Third pass. Reset node = head. For each original, take its copy and the next original, nextNode, which is copyNode!.next.
    12. Link the copies to each other: if nextNode exists, copyNode!.next = nextNode!.next. For the last pair the copy's next is already nil.
    13. Restore the original: node!.next = nextNode. Then move on with node = nextNode.
    14. Return newHead.

    The solution in Swift

    class Solution {
        func copyRandomList(_ head: ListNode?) -> ListNode? {
            if head == nil {
                return nil
            }
    
            var node = head
            while node != nil {
                let nextNode = node!.next
                let copyNode = ListNode(node!.val)
                node!.next = copyNode
                copyNode.next = nextNode
                node = nextNode
            }
    
            node = head
            while node != nil {
                let copyNode = node!.next
                if node!.random != nil {
                    copyNode!.random = node!.random!.next
                }
                node = copyNode!.next
            }
    
            let newHead = head!.next
            node = head
            while node != nil {
                let copyNode = node!.next
                let nextNode = copyNode!.next
                if nextNode != nil {
                    copyNode!.next = nextNode!.next
                }
                node!.next = nextNode
                node = nextNode
            }
    
            return newHead
        }
    }
    

    Swift details to notice:

    • Classes are references. ListNode is a class, so node!.next = copyNode changes the real node, and let copyNode is a constant reference to an object whose properties can still be assigned. That is why copyNode.next = nextNode compiles with let.
    • Two kinds of optional. The first pass's copyNode is a plain ListNode, because it was just created. In the later passes copyNode is node!.next, a ListNode?, so those passes write copyNode!.
    • The same loop condition throughout. while node != nil works on any optional without requiring ListNode to be Equatable.
    • Order matters in every assignment. Each pass first reads the links it will need later (nextNode) into constants, and only then overwrites next.

    Complexity

    Time: O(n). Three passes, each visiting every original node a constant number of times.

    Space: O(1) extra. Apart from the n new nodes, which the answer needs anyway, the code keeps a few references. The interleaving reuses the existing next fields instead of a dictionary.

    Edge cases and common mistakes

    • Empty list. The guard returns nil right away.
    • A single node. [5,0], whose random points to itself, becomes 5 → 5' after pass one, the copy's random becomes 5.next, which is 5', and the split returns one copy that points to itself.
    • Random to nil. The second pass skips the assignment, and the copy keeps its default nil.
    • Random to an earlier or later node. Every copy exists after the first pass, so direction doesn't matter.
    • Setting random links during the first pass. The target's copy might not exist yet.
    • Skipping the split. The caller's list would keep the copies in the middle, and the copy list would still be tangled with it.
    • Reading head!.next too late. After the split, head!.next is the original second node. Save newHead before the third pass.
    • Forgetting to check nextNode in the split. On the last pair nextNode is nil, and nextNode!.next would crash.

    Testing

    • [7,null], [13,0], [11,4], [10,2], [1,0] gives a list with the same values and random positions, and the original keeps its own links.
    • [1,1], [2,1] gives the same structure, built from new nodes.
    • [3,null], [3,0], [3,null] gives three new nodes with equal values and a random link only on the second, to the first.
    • An empty list gives nil.
    • [5,0] gives one new node whose random is itself.
    Keep going with SwiftAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free