Facebook Pixel

2816. Double a Number Represented as a Linked List

Problem Description

You are given the head of a non-empty linked list where the nodes together represent a non-negative integer without leading zeroes. Each node contains a single digit (0-9), and the digits are arranged from most significant to least significant when reading from head to tail.

Your task is to double the integer value represented by the linked list and return the head of a new linked list representing the doubled value.

For example:

  • If the linked list represents 189 (nodes: 1 → 8 → 9), doubling it gives 378 (nodes: 3 → 7 → 8)
  • If the linked list represents 999 (nodes: 9 → 9 → 9), doubling it gives 1998 (nodes: 1 → 9 → 9 → 8)

The solution works by:

  1. Reversing the linked list - This makes it easier to handle carry propagation since we can process from least significant digit to most significant digit
  2. Performing multiplication with carry handling - Each digit is multiplied by 2, and any carry from the previous operation is added. The result's last digit becomes the new node value, and any overflow becomes the carry for the next iteration
  3. Handling final carry - If there's a remaining carry after processing all digits, a new node is added
  4. Reversing back - The result is reversed again to restore the proper order (most significant digit first)

The key insight is that reversing the list allows us to handle the multiplication naturally from right to left (least significant to most significant), just like how we would multiply on paper.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we need to double a number represented as a linked list, we face a fundamental challenge: multiplication naturally proceeds from right to left (least significant digit to most significant), but linked lists are traversed from left to right.

Think about how you would double 189 on paper:

  189
×   2
-----
  378

You start from the rightmost digit: 9 × 2 = 18, write down 8 and carry 1. Then 8 × 2 + 1 = 17, write down 7 and carry 1. Finally 1 × 2 + 1 = 3, write down 3.

The problem is that in a singly linked list, we can't easily traverse backwards. We could use recursion to reach the end and process on the way back, but that would use O(n) stack space.

The key insight is to reverse the linked list first. This transforms our problem:

  • Original list: 1 → 8 → 9 (representing 189)
  • After reversal: 9 → 8 → 1 (digits in reverse order)

Now we can traverse the reversed list normally while performing multiplication with carry propagation, exactly like the paper method. Each node we visit corresponds to the next digit we would process when multiplying by hand.

After multiplication:

  • We get: 8 → 7 → 3 (representing 378 in reverse)
  • Reverse again: 3 → 7 → 8 (the correct answer)

This approach elegantly sidesteps the traversal limitation by aligning the list's natural left-to-right traversal with the multiplication's right-to-left processing requirement. The two reversals (O(n) each) are a small price to pay for this simplification.

Learn more about Stack, Linked List and Math patterns.

Solution Approach

The solution implements the reverse linked list and simulation approach through three main steps:

Step 1: Reverse the Linked List

The reverse helper function reverses a linked list using the standard iterative approach:

def reverse(head):
    dummy = ListNode()
    cur = head
    while cur:
        next = cur.next         # Save next node
        cur.next = dummy.next   # Point current to previous reversed part
        dummy.next = cur        # Update dummy to point to current
        cur = next              # Move to next node
    return dummy.next

This uses a dummy node to simplify the reversal process. Each node is detached from the original list and inserted at the beginning of the new reversed list.

Step 2: Simulate Multiplication with Carry

After reversing the input list, we traverse it while maintaining:

  • mul = 2: The multiplication factor
  • carry = 0: Tracks overflow from previous digit multiplication
  • dummy and cur: Build the result list
dummy = cur = ListNode()
mul, carry = 2, 0
while head:
    x = head.val * mul + carry  # Multiply digit and add carry
    carry = x // 10              # Calculate new carry (0 or 1)
    cur.next = ListNode(x % 10) # Create node with last digit
    cur = cur.next               # Move result pointer
    head = head.next             # Move input pointer

For each digit, we:

  1. Calculate x = digit × 2 + carry
  2. Extract the new carry using integer division: carry = x // 10
  3. Create a new node with the ones digit: x % 10

Step 3: Handle Final Carry and Reverse Back

After processing all digits, if there's still a carry (meaning the number of digits increased), we add one more node:

if carry:
    cur.next = ListNode(carry)

Finally, we reverse the result list to restore the proper digit order:

return reverse(dummy.next)

Time and Space Complexity:

  • Time: O(n) where n is the number of nodes - we traverse the list three times (reverse, multiply, reverse)
  • Space: O(n) for creating the new result list

The elegance of this solution lies in transforming the problem through reversals, allowing us to handle carry propagation naturally while traversing the list in its standard direction.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through doubling the number 189 represented as a linked list: 1 → 8 → 9

Step 1: Reverse the Original List

Starting with 1 → 8 → 9, we reverse it to get 9 → 8 → 1

This allows us to process digits from least significant (9) to most significant (1).

Step 2: Multiply Each Digit with Carry Handling

Now we traverse the reversed list and multiply by 2:

  • Process node 9:

    • Calculate: 9 × 2 + 0 (carry) = 18
    • New carry: 18 // 10 = 1
    • Create node with value: 18 % 10 = 8
    • Result so far: 8
  • Process node 8:

    • Calculate: 8 × 2 + 1 (carry) = 17
    • New carry: 17 // 10 = 1
    • Create node with value: 17 % 10 = 7
    • Result so far: 8 → 7
  • Process node 1:

    • Calculate: 1 × 2 + 1 (carry) = 3
    • New carry: 3 // 10 = 0
    • Create node with value: 3 % 10 = 3
    • Result so far: 8 → 7 → 3

Step 3: Check Final Carry

The carry is 0, so no additional node is needed.

Step 4: Reverse the Result

We have 8 → 7 → 3 (which represents 378 in reverse). Reversing it gives us 3 → 7 → 8, which correctly represents 378.

Final Answer: 3 → 7 → 8

This matches our expected result since 189 × 2 = 378.

Solution Implementation

1# Definition for singly-linked list.
2# class ListNode:
3#     def __init__(self, val=0, next=None):
4#         self.val = val
5#         self.next = next
6
7class Solution:
8    def doubleIt(self, head: Optional[ListNode]) -> Optional[ListNode]:
9        """
10        Doubles the number represented by a linked list.
11        The linked list represents a number where each node contains a single digit.
12        """
13      
14        def reverse_list(head: Optional[ListNode]) -> Optional[ListNode]:
15            """
16            Reverses a linked list and returns the new head.
17            """
18            dummy = ListNode()
19            current = head
20          
21            while current:
22                # Store the next node
23                next_node = current.next
24                # Insert current node at the beginning of reversed list
25                current.next = dummy.next
26                dummy.next = current
27                # Move to the next node
28                current = next_node
29          
30            return dummy.next
31      
32        # Reverse the list to process from least significant digit
33        reversed_head = reverse_list(head)
34      
35        # Create dummy node for result list
36        dummy = ListNode()
37        current = dummy
38      
39        # Initialize multiplier and carry
40        multiplier = 2
41        carry = 0
42      
43        # Process each digit
44        while reversed_head:
45            # Calculate the product and add carry
46            product = reversed_head.val * multiplier + carry
47            # Update carry for next iteration
48            carry = product // 10
49            # Create new node with the digit (ones place)
50            current.next = ListNode(product % 10)
51            # Move pointers forward
52            current = current.next
53            reversed_head = reversed_head.next
54      
55        # Add remaining carry as a new node if exists
56        if carry:
57            current.next = ListNode(carry)
58      
59        # Reverse the result list to get correct order
60        return reverse_list(dummy.next)
61
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 *     int val;
5 *     ListNode next;
6 *     ListNode() {}
7 *     ListNode(int val) { this.val = val; }
8 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12    /**
13     * Doubles the number represented by a linked list.
14     * The linked list represents a number where each node contains a single digit.
15     * 
16     * @param head The head of the linked list representing the number
17     * @return The head of the new linked list representing the doubled number
18     */
19    public ListNode doubleIt(ListNode head) {
20        // Reverse the linked list to process from least significant digit
21        head = reverse(head);
22      
23        // Create a dummy node to simplify list construction
24        ListNode dummyHead = new ListNode();
25        ListNode current = dummyHead;
26      
27        // Initialize multiplier and carry for multiplication
28        int multiplier = 2;
29        int carry = 0;
30      
31        // Process each digit in the reversed list
32        while (head != null) {
33            // Calculate the product of current digit with multiplier plus any carry
34            int product = head.val * multiplier + carry;
35          
36            // Update carry for next iteration (integer division by 10)
37            carry = product / 10;
38          
39            // Create new node with the ones digit of the product
40            current.next = new ListNode(product % 10);
41          
42            // Move to next position in result list
43            current = current.next;
44          
45            // Move to next digit in input list
46            head = head.next;
47        }
48      
49        // If there's remaining carry, add it as a new node
50        if (carry > 0) {
51            current.next = new ListNode(carry);
52        }
53      
54        // Reverse the result list to get the correct order
55        return reverse(dummyHead.next);
56    }
57
58    /**
59     * Reverses a linked list.
60     * 
61     * @param head The head of the linked list to reverse
62     * @return The head of the reversed linked list
63     */
64    private ListNode reverse(ListNode head) {
65        // Create a dummy node to build the reversed list
66        ListNode dummyHead = new ListNode();
67        ListNode current = head;
68      
69        // Iterate through the original list
70        while (current != null) {
71            // Store the next node before modifying pointers
72            ListNode nextNode = current.next;
73          
74            // Insert current node at the beginning of the reversed list
75            current.next = dummyHead.next;
76            dummyHead.next = current;
77          
78            // Move to the next node in the original list
79            current = nextNode;
80        }
81      
82        // Return the head of the reversed list
83        return dummyHead.next;
84    }
85}
86
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 *     int val;
5 *     ListNode *next;
6 *     ListNode() : val(0), next(nullptr) {}
7 *     ListNode(int x) : val(x), next(nullptr) {}
8 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13    /**
14     * Doubles the number represented by a linked list
15     * @param head: head of the linked list representing a number
16     * @return: head of the new linked list representing the doubled number
17     */
18    ListNode* doubleIt(ListNode* head) {
19        // Step 1: Reverse the linked list to process from least significant digit
20        head = reverse(head);
21      
22        // Create a dummy head for the result linked list
23        ListNode* dummyHead = new ListNode();
24        ListNode* currentNode = dummyHead;
25      
26        // Initialize multiplier and carry for multiplication
27        int multiplier = 2;
28        int carry = 0;
29      
30        // Step 2: Traverse the reversed list and multiply each digit by 2
31        while (head != nullptr) {
32            // Calculate current digit value (digit * 2 + carry)
33            int product = head->val * multiplier + carry;
34          
35            // Update carry for next iteration
36            carry = product / 10;
37          
38            // Create new node with the digit (product % 10)
39            currentNode->next = new ListNode(product % 10);
40            currentNode = currentNode->next;
41          
42            // Move to next node in original list
43            head = head->next;
44        }
45      
46        // Step 3: Handle remaining carry if exists
47        if (carry > 0) {
48            currentNode->next = new ListNode(carry);
49        }
50      
51        // Step 4: Reverse the result to get correct order
52        return reverse(dummyHead->next);
53    }
54
55private:
56    /**
57     * Reverses a linked list
58     * @param head: head of the linked list to reverse
59     * @return: head of the reversed linked list
60     */
61    ListNode* reverse(ListNode* head) {
62        // Use dummy head to simplify the reversal logic
63        ListNode* dummyHead = new ListNode();
64        ListNode* currentNode = head;
65      
66        // Iterate through the list and reverse pointers
67        while (currentNode != nullptr) {
68            // Store next node before modifying pointer
69            ListNode* nextNode = currentNode->next;
70          
71            // Insert current node at the beginning of reversed list
72            currentNode->next = dummyHead->next;
73            dummyHead->next = currentNode;
74          
75            // Move to next node in original list
76            currentNode = nextNode;
77        }
78      
79        return dummyHead->next;
80    }
81};
82
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 *     val: number
5 *     next: ListNode | null
6 *     constructor(val?: number, next?: ListNode | null) {
7 *         this.val = (val===undefined ? 0 : val)
8 *         this.next = (next===undefined ? null : next)
9 *     }
10 * }
11 */
12
13/**
14 * Doubles the number represented by a linked list
15 * @param head - The head of the linked list representing a number
16 * @returns The head of the new linked list representing the doubled number
17 */
18function doubleIt(head: ListNode | null): ListNode | null {
19    // Reverse the linked list to process from least significant digit
20    head = reverse(head);
21  
22    // Create a dummy node to simplify list construction
23    const dummyNode: ListNode = new ListNode();
24    let currentNode: ListNode = dummyNode;
25  
26    // Initialize multiplier and carry for multiplication
27    const multiplier: number = 2;
28    let carry: number = 0;
29  
30    // Process each digit in the reversed list
31    while (head !== null) {
32        // Multiply current digit by 2 and add carry
33        const product: number = head.val * multiplier + carry;
34      
35        // Calculate new carry for next iteration
36        carry = Math.floor(product / 10);
37      
38        // Create new node with the digit (product % 10)
39        currentNode.next = new ListNode(product % 10);
40        currentNode = currentNode.next;
41      
42        // Move to next digit
43        head = head.next;
44    }
45  
46    // If there's remaining carry, add it as a new node
47    if (carry > 0) {
48        currentNode.next = new ListNode(carry);
49    }
50  
51    // Reverse the result to get the correct order
52    return reverse(dummyNode.next);
53}
54
55/**
56 * Reverses a linked list
57 * @param head - The head of the linked list to reverse
58 * @returns The head of the reversed linked list
59 */
60function reverse(head: ListNode | null): ListNode | null {
61    // Create a dummy node to simplify the reversal process
62    const dummyNode: ListNode = new ListNode();
63    let currentNode: ListNode | null = head;
64  
65    // Iterate through the original list
66    while (currentNode !== null) {
67        // Store the next node before breaking the link
68        const nextNode: ListNode | null = currentNode.next;
69      
70        // Insert current node at the beginning of the reversed list
71        currentNode.next = dummyNode.next;
72        dummyNode.next = currentNode;
73      
74        // Move to the next node in the original list
75        currentNode = nextNode;
76    }
77  
78    return dummyNode.next;
79}
80

Time and Space Complexity

The time complexity is O(n), where n is the length of the linked list. The algorithm performs three main operations:

  1. First reverse() call traverses the entire list once: O(n)
  2. The main while loop processes each node exactly once to perform multiplication: O(n)
  3. Second reverse() call traverses the result list once: O(n)

Total time complexity: O(n) + O(n) + O(n) = O(n)

The space complexity is O(1) if we exclude the space required for the output linked list. The algorithm uses only a constant amount of extra space:

  • A few pointer variables (dummy, cur, next, head) in the reverse function
  • Variables mul, carry, and x for the multiplication logic
  • Temporary ListNode objects for dummy nodes

All these variables use constant space regardless of the input size. The new linked list created for the result is not counted as it's part of the required output.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Forgetting to Handle the Final Carry

The Pitfall: One of the most common mistakes is forgetting to add the final carry after processing all digits. This happens when doubling a number causes it to gain an extra digit.

Example of the Bug:

# INCORRECT - Missing final carry handling
while reversed_head:
    product = reversed_head.val * multiplier + carry
    carry = product // 10
    current.next = ListNode(product % 10)
    current = current.next
    reversed_head = reversed_head.next
# Forgot to check if carry > 0 here!
return reverse_list(dummy.next)

When input is 999 (9→9→9), this would incorrectly return 998 instead of 1998, losing the leading 1.

The Fix: Always check for remaining carry after the main loop:

if carry:
    current.next = ListNode(carry)

2. Modifying the Original List Instead of Creating a New One

The Pitfall: Attempting to modify the original list in-place during reversal or multiplication operations, which can corrupt the input data or lead to infinite loops.

Example of the Bug:

# INCORRECT - Modifying original nodes
def doubleIt(self, head):
    head = reverse_list(head)
    current = head
    carry = 0
    while current:
        product = current.val * 2 + carry
        carry = product // 10
        current.val = product % 10  # Modifying original node!
        current = current.next

The Fix: Always create new nodes for the result:

current.next = ListNode(product % 10)  # Create new node

3. Incorrect Reversal Implementation

The Pitfall: Using an incorrect reversal algorithm that loses nodes or creates cycles. A common mistake is not properly tracking the next node before modifying pointers.

Example of the Bug:

# INCORRECT - Loses reference to rest of list
def reverse_list(head):
    prev = None
    while head:
        head.next = prev  # Lost reference to original head.next!
        prev = head
        head = head.next  # This is now None or prev

The Fix: Save the next node before modifying pointers:

def reverse_list(head):
    dummy = ListNode()
    current = head
    while current:
        next_node = current.next  # Save next first!
        current.next = dummy.next
        dummy.next = current
        current = next_node  # Use saved reference

4. Not Handling Edge Cases with Single Digit Overflow

The Pitfall: Assuming carry will always be at most 1, when in fact it can only be 0 or 1 for doubling operations (since max is 9 × 2 + 1 = 19).

Example of Potential Confusion:

# Overly complex - carry for doubling is always 0 or 1
if carry > 9:  # This will never happen
    # Unnecessary complex logic

The Fix: Keep it simple - carry for doubling will only be 0 or 1:

carry = product // 10  # Will be 0 or 1
current.next = ListNode(product % 10)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the maximum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More