2. Add Two Numbers
Problem Description
You need to add two non-negative integers that are represented as linked lists. The key characteristics of this problem are:
-
Input Format: Two linked lists where each node contains a single digit (0-9)
-
Digit Storage: The digits are stored in reverse order, meaning the ones place is at the head of the list
- For example: The number
342
would be represented as2 -> 4 -> 3
- The number
465
would be represented as5 -> 6 -> 4
- For example: The number
-
Task: Add these two numbers and return the sum as a new linked list (also in reverse order)
- Using the example above:
342 + 465 = 807
, which should be returned as7 -> 0 -> 8
- Using the example above:
-
Constraints:
- Both linked lists are non-empty
- The numbers don't have leading zeros (except for the number 0 itself)
The solution simulates the grade-school addition algorithm. We traverse both linked lists simultaneously from head to tail, adding corresponding digits along with any carry from the previous position. The process continues until both lists are exhausted and there's no remaining carry.
The algorithm handles lists of different lengths by treating missing nodes as having a value of 0
. For each position, it:
- Calculates the sum:
current_digit_l1 + current_digit_l2 + carry
- Determines the new digit:
sum % 10
- Updates the carry:
sum // 10
- Creates a new node with the calculated digit and adds it to the result list
Intuition
The key insight is recognizing that the linked lists store digits in reverse order, which actually makes the addition process more natural. When we add numbers by hand, we start from the rightmost digit (ones place) and move left, carrying over when needed. Since our linked lists already start with the ones place at the head, we can directly simulate this addition process.
Think about how you would add 342 + 465
on paper:
342
+ 465
-----
807
You'd start from the right: 2 + 5 = 7
, then 4 + 6 = 10
(write 0
, carry 1
), then 3 + 4 + 1 = 8
.
With our reversed linked lists (2->4->3
and 5->6->4
), we can traverse from head to tail and perform the exact same operations in the same order. This eliminates the need to reverse the lists first or use extra space to store intermediate results.
The elegance of this approach comes from handling three cases uniformly:
- When both lists have digits to process
- When one list is longer than the other (treat missing digits as
0
) - When we have a final carry after processing all digits (like when adding
99 + 1 = 100
)
By using a dummy head node, we avoid special-casing the creation of the first node in our result list. The while l1 or l2 or carry
condition ensures we continue as long as there's any work left to do, whether it's processing remaining digits from either list or handling a final carry.
Learn more about Recursion, Linked List and Math patterns.
Solution Approach
The solution uses a simulation approach to add the two numbers digit by digit, just like manual addition. Here's how the implementation works:
Data Structure Setup:
- Create a
dummy
node to simplify list construction (avoids special handling for the first node) - Initialize
carry = 0
to track carry-over between digit additions - Set
curr
pointer todummy
to track where we're building the result list
Main Algorithm Loop:
The while l1 or l2 or carry
condition ensures we continue processing as long as:
- There are digits remaining in
l1
, OR - There are digits remaining in
l2
, OR - There's a carry value that needs to be added
For each iteration:
-
Calculate the sum:
s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
This safely handles cases where one list is shorter by treating missing nodes as
0
-
Extract digit and carry:
carry, val = divmod(s, 10)
divmod(s, 10)
returns(quotient, remainder)
, giving us the new carry and the digit to store -
Build the result:
curr.next = ListNode(val) curr = curr.next
Create a new node with the calculated digit and advance our pointer
-
Advance input pointers:
l1 = l1.next if l1 else None l2 = l2.next if l2 else None
Move to the next digit in each list if available
Return Result:
Return dummy.next
to skip the dummy head and provide the actual result list.
Time Complexity: O(max(m, n))
where m
and n
are the lengths of the two input lists
Space Complexity: O(max(m, n))
for the new linked list storing the result
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through adding 342 + 465 = 807
step by step.
Input Lists:
l1
:2 -> 4 -> 3
(represents 342)l2
:5 -> 6 -> 4
(represents 465)
Initial Setup:
- Create
dummy
node (placeholder head) carry = 0
curr
points todummy
Iteration 1:
- Both lists have nodes:
l1.val = 2
,l2.val = 5
- Calculate sum:
2 + 5 + 0 = 7
carry = 7 // 10 = 0
,digit = 7 % 10 = 7
- Create new node with value
7
, attach to result - Advance:
l1
to4
,l2
to6
- Result so far:
dummy -> 7
Iteration 2:
- Both lists have nodes:
l1.val = 4
,l2.val = 6
- Calculate sum:
4 + 6 + 0 = 10
carry = 10 // 10 = 1
,digit = 10 % 10 = 0
- Create new node with value
0
, attach to result - Advance:
l1
to3
,l2
to4
- Result so far:
dummy -> 7 -> 0
Iteration 3:
- Both lists have nodes:
l1.val = 3
,l2.val = 4
- Calculate sum:
3 + 4 + 1 = 8
carry = 8 // 10 = 0
,digit = 8 % 10 = 8
- Create new node with value
8
, attach to result - Advance:
l1
toNone
,l2
toNone
- Result so far:
dummy -> 7 -> 0 -> 8
Iteration 4:
- Both
l1
andl2
areNone
,carry = 0
- Loop condition
(l1 or l2 or carry)
isFalse
, exit loop
Final Result:
Return dummy.next
which gives us 7 -> 0 -> 8
(represents 807)
This correctly computes 342 + 465 = 807
.
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
7from typing import Optional
8
9class Solution:
10 def addTwoNumbers(
11 self, l1: Optional[ListNode], l2: Optional[ListNode]
12 ) -> Optional[ListNode]:
13 """
14 Add two numbers represented as linked lists where each node contains a single digit.
15 The digits are stored in reverse order (least significant digit first).
16
17 Args:
18 l1: First linked list representing a number
19 l2: Second linked list representing a number
20
21 Returns:
22 A linked list representing the sum of the two numbers
23 """
24 # Create a dummy head node to simplify list construction
25 dummy_head = ListNode()
26 carry = 0
27 current_node = dummy_head
28
29 # Process both lists while either has remaining digits or there's a carry
30 while l1 or l2 or carry:
31 # Get the current digit from each list (0 if list is exhausted)
32 digit1 = l1.val if l1 else 0
33 digit2 = l2.val if l2 else 0
34
35 # Calculate sum of current digits plus any carry from previous addition
36 total_sum = digit1 + digit2 + carry
37
38 # Calculate new carry and the digit to store in current position
39 carry, digit_value = divmod(total_sum, 10)
40
41 # Create new node with the calculated digit and link it
42 current_node.next = ListNode(digit_value)
43 current_node = current_node.next
44
45 # Move to next nodes in input lists if they exist
46 l1 = l1.next if l1 else None
47 l2 = l2.next if l2 else None
48
49 # Return the actual result list (skip the dummy head)
50 return dummy_head.next
51
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 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
13 // Create a dummy head node to simplify list construction
14 ListNode dummyHead = new ListNode(0);
15
16 // Initialize carry for addition overflow
17 int carry = 0;
18
19 // Pointer to track current position in result list
20 ListNode current = dummyHead;
21
22 // Continue while there are digits to process or carry exists
23 while (l1 != null || l2 != null || carry != 0) {
24 // Get current digit values (0 if node is null)
25 int digit1 = (l1 == null) ? 0 : l1.val;
26 int digit2 = (l2 == null) ? 0 : l2.val;
27
28 // Calculate sum of current digits plus carry
29 int sum = digit1 + digit2 + carry;
30
31 // Update carry for next iteration (integer division)
32 carry = sum / 10;
33
34 // Create new node with the current digit (remainder after division by 10)
35 current.next = new ListNode(sum % 10);
36
37 // Move current pointer to the newly created node
38 current = current.next;
39
40 // Move to next nodes in input lists if they exist
41 if (l1 != null) {
42 l1 = l1.next;
43 }
44 if (l2 != null) {
45 l2 = l2.next;
46 }
47 }
48
49 // Return the actual head of result list (skip dummy head)
50 return dummyHead.next;
51 }
52}
53
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 ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
14 // Create a dummy head node to simplify list construction
15 ListNode dummyHead;
16
17 // Initialize carry for handling digit overflow
18 int carry = 0;
19
20 // Pointer to track the current position in the result list
21 ListNode* current = &dummyHead;
22
23 // Continue while there are digits in either list or a carry remains
24 while (l1 != nullptr || l2 != nullptr || carry != 0) {
25 // Calculate sum of current digits plus any carry from previous addition
26 int sum = 0;
27
28 // Add digit from first list if available
29 if (l1 != nullptr) {
30 sum += l1->val;
31 l1 = l1->next;
32 }
33
34 // Add digit from second list if available
35 if (l2 != nullptr) {
36 sum += l2->val;
37 l2 = l2->next;
38 }
39
40 // Add carry from previous calculation
41 sum += carry;
42
43 // Update carry for next iteration (sum / 10)
44 carry = sum / 10;
45
46 // Create new node with the digit (sum % 10) and append to result
47 current->next = new ListNode(sum % 10);
48
49 // Move current pointer to the newly created node
50 current = current->next;
51 }
52
53 // Return the actual head of the result list (skip dummy head)
54 return dummyHead.next;
55 }
56};
57
1/**
2 * Definition for singly-linked list node
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 * Adds two numbers represented as linked lists
15 * Each node contains a single digit and the digits are stored in reverse order
16 * @param l1 - First linked list representing a number
17 * @param l2 - Second linked list representing a number
18 * @returns A linked list representing the sum of the two numbers
19 */
20function addTwoNumbers(l1: ListNode | null, l2: ListNode | null): ListNode | null {
21 // Create a dummy head node to simplify list construction
22 const dummyHead: ListNode = new ListNode();
23
24 // Pointer to track the current position in the result list
25 let currentNode: ListNode = dummyHead;
26
27 // Variable to store the carry value and current sum
28 let carryOver: number = 0;
29
30 // Process both lists until all nodes are visited and no carry remains
31 while (l1 !== null || l2 !== null || carryOver !== 0) {
32 // Add value from first list if available
33 if (l1 !== null) {
34 carryOver += l1.val;
35 l1 = l1.next;
36 }
37
38 // Add value from second list if available
39 if (l2 !== null) {
40 carryOver += l2.val;
41 l2 = l2.next;
42 }
43
44 // Create new node with the digit (sum % 10)
45 currentNode.next = new ListNode(carryOver % 10);
46
47 // Move pointer to the newly created node
48 currentNode = currentNode.next;
49
50 // Calculate carry for the next iteration
51 carryOver = Math.floor(carryOver / 10);
52 }
53
54 // Return the actual result list (skip dummy head)
55 return dummyHead.next;
56}
57
Time and Space Complexity
Time Complexity: O(max(m, n))
, where m
and n
are the lengths of the two linked lists l1
and l2
respectively. The algorithm traverses both linked lists simultaneously until both are exhausted and there's no carry. In each iteration, we perform constant time operations (addition, modulo, division, and node creation), so each position requires O(1)
time. The total number of iterations is at most max(m, n) + 1
(the +1 accounts for a potential final carry), which simplifies to O(max(m, n))
.
Space Complexity: O(max(m, n))
for the output linked list that stores the result, which will have at most max(m, n) + 1
nodes. However, if we don't count the space used for the output (as it's required for the answer), the auxiliary space complexity is O(1)
since we only use a constant amount of extra space for variables like dummy
, carry
, and curr
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle the Final Carry
One of the most common mistakes is forgetting that after processing all digits from both lists, there might still be a carry that needs to be added as a new node.
Incorrect Implementation:
while l1 or l2: # Missing carry check!
s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
carry, val = divmod(s, 10)
curr.next = ListNode(val)
curr = curr.next
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
# If carry is 1 here, it's lost!
Example where this fails:
- Input:
l1 = [9, 9]
(represents 99),l2 = [1]
(represents 1) - Expected:
[0, 0, 1]
(represents 100) - Incorrect result:
[0, 0]
(represents 00, missing the final carry)
Solution: Always include carry
in the loop condition: while l1 or l2 or carry:
2. Modifying Input Lists Instead of Creating New Nodes
Some implementations try to reuse nodes from the input lists, which can cause issues if the original lists need to be preserved.
Problematic Approach:
# Trying to modify l1 in place
while l1 or l2 or carry:
s = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
carry, val = divmod(s, 10)
if l1:
l1.val = val # Modifying original list!
curr = l1
else:
curr.next = ListNode(val)
curr = curr.next
# ... rest of logic
Solution: Always create new ListNode
objects for the result to keep the input lists unchanged.
3. Incorrectly Handling Lists of Different Lengths
Failing to properly handle cases where one list is shorter than the other.
Common Mistake:
while l1 and l2: # Only processes while BOTH have nodes
s = l1.val + l2.val + carry
# ... process
l1 = l1.next
l2 = l2.next
# Then trying to handle remaining nodes separately - error prone!
while l1:
s = l1.val + carry
# ... duplicate logic
while l2:
s = l2.val + carry
# ... more duplicate logic
Solution: Use a single loop with conditional checks: (l1.val if l1 else 0) + (l2.val if l2 else 0)
4. Not Using a Dummy Head Node
Trying to handle the first node as a special case leads to complex and error-prone code.
Without Dummy Head:
result = None
curr = None
while l1 or l2 or carry:
# ... calculate val
new_node = ListNode(val)
if result is None: # Special handling for first node
result = new_node
curr = result
else:
curr.next = new_node
curr = curr.next
# ...
return result
Solution: Using a dummy head simplifies the logic significantly and eliminates special case handling.
Which data structure is used to implement priority queue?
Recommended Readings
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
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!