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 gives378
(nodes: 3 → 7 → 8) - If the linked list represents
999
(nodes: 9 → 9 → 9), doubling it gives1998
(nodes: 1 → 9 → 9 → 8)
The solution works by:
- Reversing the linked list - This makes it easier to handle carry propagation since we can process from least significant digit to most significant digit
- 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
- Handling final carry - If there's a remaining carry after processing all digits, a new node is added
- 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.
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 factorcarry = 0
: Tracks overflow from previous digit multiplicationdummy
andcur
: 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:
- Calculate
x = digit × 2 + carry
- Extract the new carry using integer division:
carry = x // 10
- 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 EvaluatorExample 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
- Calculate:
-
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
- Calculate:
-
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
- Calculate:
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:
- First
reverse()
call traverses the entire list once:O(n)
- The main while loop processes each node exactly once to perform multiplication:
O(n)
- 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
, andx
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)
In a binary min heap, the maximum element can be found in:
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Want a Structured Path to Master System Design Too? Don’t Miss This!