0002. Add Two Numbers¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Brute Force (Reconstruct Numbers)
- Approach 2: Optimal (Simultaneous Traversal with Carry)
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 2. Add Two Numbers |
| Difficulty | 🟡 Medium |
| Tags | Linked List, Math, Recursion |
Description¶
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Examples¶
Example 1:
Input: l1 = [2,4,3], l2 = [5,6,4]
Output: [7,0,8]
Explanation: 342 + 465 = 807. Stored in reverse: [7,0,8]
Example 2:
Input: l1 = [0], l2 = [0]
Output: [0]
Example 3:
Input: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
Output: [8,9,9,9,0,0,0,1]
Explanation: 9999999 + 9999 = 10009998. Stored in reverse: [8,9,9,9,0,0,0,1]
Constraints¶
- The number of nodes in each linked list is in the range
[1, 100] 0 <= Node.val <= 9- It is guaranteed that the list represents a number that does not have leading zeros
Problem Breakdown¶
1. What is being asked?¶
Two linked lists each represent a non-negative integer with digits in reverse order (least significant digit first). Add them as integers and return the result as a new linked list in the same reversed format.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
l1 | ListNode | Head of first linked list (digits in reverse order) |
l2 | ListNode | Head of second linked list (digits in reverse order) |
Important observations about the input: - Digits are stored least-significant first — this is actually convenient for addition (we add from the smallest digit) - Both lists are non-empty (at least one node each) - Each node holds exactly one digit 0–9 - No leading zeros in the number, except the number 0 itself
3. What is the output?¶
- A new linked list representing the sum, also in reverse order (least-significant first)
- The result list may be longer than either input if there is a final carry
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
1 <= nodes <= 100 | At most 100 digits → numbers up to 10^100; must avoid integer overflow in languages with fixed-width integers |
0 <= Node.val <= 9 | Each node is a valid decimal digit |
| No leading zeros | The only zero-only list is [0] |
5. Step-by-step example analysis¶
Example 1: l1 = [2,4,3], l2 = [5,6,4] → [7,0,8]¶
l1 represents: 342 (digits reversed: 2 → 4 → 3)
l2 represents: 465 (digits reversed: 5 → 6 → 4)
Column-by-column addition (right-to-left in normal math = left-to-right in lists):
Position 0 (ones): 2 + 5 = 7, carry = 0 → digit: 7
Position 1 (tens): 4 + 6 = 10, carry = 1 → digit: 0
Position 2 (hundreds): 3 + 4 + 1(carry) = 8, carry = 0 → digit: 8
Result list: [7, 0, 8] represents: 807
Example 3: l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] → [8,9,9,9,0,0,0,1]¶
l1: 9999999
l2: 9999
Pos 0: 9+9=18, carry=1 → digit 8
Pos 1: 9+9+1=19, carry=1 → digit 9
Pos 2: 9+9+1=19, carry=1 → digit 9
Pos 3: 9+9+1=19, carry=1 → digit 9
Pos 4: 9+0+1=10, carry=1 → digit 0
Pos 5: 9+0+1=10, carry=1 → digit 0
Pos 6: 9+0+1=10, carry=1 → digit 0
Pos 7: 0+0+1=1, carry=0 → digit 1 (extra node from final carry)
Result: [8, 9, 9, 9, 0, 0, 0, 1] represents: 10009998
6. Key Observations¶
- Reverse order is convenient — the head of each list is already the least significant digit, matching how we do addition column by column.
- Carry — when the digit sum exceeds 9, we must carry 1 to the next position.
- Different lengths — one list may be shorter; treat missing digits as 0.
- Final carry — after both lists are exhausted, if carry is still 1, append one more node.
- Dummy head trick — using a dummy (sentinel) node avoids special-casing the first node.
7. Pattern identification¶
| Pattern | Why it fits | Example |
|---|---|---|
| Linked List Traversal | Process nodes one-by-one from both lists | Add Two Numbers (this problem) |
| Carry Propagation | Classic digit-by-digit addition | All arithmetic on digit arrays |
| Dummy Head | Simplifies list construction | Merge Two Sorted Lists |
Chosen pattern: Simultaneous traversal with carry Reason: The digits are already in the right order for addition. One pass suffices — O(max(m,n)) time, no need to reconstruct actual integers.
Approach 1: Brute Force (Reconstruct Numbers)¶
Thought process¶
Convert each linked list into a real integer, add the integers normally, then convert the result back into a linked list. Simple conceptually, but fails in practice for very long lists (100 digits) due to integer overflow in most languages.
Algorithm (step-by-step)¶
- Traverse
l1, reconstruct the integer (remember digits are reversed) - Traverse
l2, reconstruct the integer - Add the two integers
- Convert the sum digit by digit back into a linked list (in reverse order again)
Pseudocode¶
function addTwoNumbers(l1, l2):
num1 = 0, multiplier = 1
for each node in l1:
num1 += node.val * multiplier
multiplier *= 10
num2 = 0, multiplier = 1
for each node in l2:
num2 += node.val * multiplier
multiplier *= 10
total = num1 + num2
if total == 0: return ListNode(0)
dummy = ListNode(0)
curr = dummy
while total > 0:
curr.next = ListNode(total % 10)
curr = curr.next
total //= 10
return dummy.next
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(m+n) | Two passes to read lists + one pass to build result |
| Space | O(max(m,n)) | Result list length |
Warning: This approach causes integer overflow in Go and Java for inputs with 100 digits, since the number can be as large as 10^100. Python handles arbitrary precision, but the approach is not general.
Implementation¶
Go¶
// addTwoNumbers — Brute Force (reconstruct numbers)
// WARNING: overflows for large inputs (>18 digits)
// Time: O(m+n), Space: O(max(m,n))
func addTwoNumbersBrute(l1 *ListNode, l2 *ListNode) *ListNode {
// Reconstruct integer from l1
num1, mult := 0, 1
for l1 != nil {
num1 += l1.Val * mult
mult *= 10
l1 = l1.Next
}
// Reconstruct integer from l2
num2, mult := 0, 1
for l2 != nil {
num2 += l2.Val * mult
mult *= 10
l2 = l2.Next
}
total := num1 + num2
// Edge case: sum is zero
if total == 0 {
return &ListNode{Val: 0}
}
// Build result list
dummy := &ListNode{}
curr := dummy
for total > 0 {
curr.Next = &ListNode{Val: total % 10}
curr = curr.Next
total /= 10
}
return dummy.Next
}
Java¶
// addTwoNumbers — Brute Force (reconstruct numbers)
// WARNING: overflows for large inputs; use BigInteger in production
// Time: O(m+n), Space: O(max(m,n))
public ListNode addTwoNumbersBrute(ListNode l1, ListNode l2) {
long num1 = 0, mult = 1;
while (l1 != null) {
num1 += l1.val * mult;
mult *= 10;
l1 = l1.next;
}
long num2 = 0; mult = 1;
while (l2 != null) {
num2 += l2.val * mult;
mult *= 10;
l2 = l2.next;
}
long total = num1 + num2;
if (total == 0) return new ListNode(0);
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
while (total > 0) {
curr.next = new ListNode((int)(total % 10));
curr = curr.next;
total /= 10;
}
return dummy.next;
}
Python¶
def addTwoNumbersBrute(self, l1, l2):
"""
Brute Force — reconstruct integers (Python handles big ints natively)
Time: O(m+n), Space: O(max(m,n))
"""
num1, mult = 0, 1
while l1:
num1 += l1.val * mult
mult *= 10
l1 = l1.next
num2, mult = 0, 1
while l2:
num2 += l2.val * mult
mult *= 10
l2 = l2.next
total = num1 + num2
if total == 0:
return ListNode(0)
dummy = ListNode()
curr = dummy
while total > 0:
curr.next = ListNode(total % 10)
curr = curr.next
total //= 10
return dummy.next
Dry Run¶
Input: l1 = [2,4,3], l2 = [5,6,4]
l1 → num1:
pos 0: 2 * 1 = 2
pos 1: 4 * 10 = 40
pos 2: 3 * 100 = 300
num1 = 342
l2 → num2:
pos 0: 5 * 1 = 5
pos 1: 6 * 10 = 60
pos 2: 4 * 100 = 400
num2 = 465
total = 342 + 465 = 807
Build list:
807 % 10 = 7, 807 / 10 = 80
80 % 10 = 0, 80 / 10 = 8
8 % 10 = 8, 8 / 10 = 0
Result: [7, 0, 8] ✅
Approach 2: Optimal (Simultaneous Traversal with Carry)¶
The problem with Brute Force¶
Reconstructing integers overflows for lists with up to 100 nodes (10^100). Question: Can we add directly on the list nodes without building a big integer?
Optimization idea¶
Digit-by-digit addition! Just like how you add two numbers by hand on paper — column by column from right to left. Since the lists are already least-significant first, we can traverse both simultaneously and build the result on the fly.
Key insight: - At each step:
sum = l1.val + l2.val + carry- New digit:sum % 10- New carry:sum / 10- Keep going until both lists end AND carry is 0
Algorithm (step-by-step)¶
- Create a dummy head node (sentinel) and set
curr = dummy,carry = 0 - Loop while
l1 != nil OR l2 != nil OR carry != 0: a.sum = carryb. Ifl1 != nil:sum += l1.val, advancel1c. Ifl2 != nil:sum += l2.val, advancel2d.carry = sum / 10,digit = sum % 10e. AppendListNode(digit)to result, advancecurr - Return
dummy.next
Pseudocode¶
function addTwoNumbers(l1, l2):
dummy = ListNode(0)
curr = dummy
carry = 0
while l1 != null OR l2 != null OR carry != 0:
sum = carry
if l1 != null: sum += l1.val; l1 = l1.next
if l2 != null: sum += l2.val; l2 = l2.next
carry = sum / 10
digit = sum % 10
curr.next = ListNode(digit)
curr = curr.next
return dummy.next
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(max(m,n)) | Single pass through both lists simultaneously |
| Space | O(max(m,n)) | Result list has at most max(m,n)+1 nodes (for final carry) |
Implementation¶
Go¶
// addTwoNumbers — Optimal: simultaneous traversal with carry
// Time: O(max(m,n)), Space: O(max(m,n))
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
dummy := &ListNode{}
curr := dummy
carry := 0
for l1 != nil || l2 != nil || carry != 0 {
sum := carry
if l1 != nil {
sum += l1.Val
l1 = l1.Next
}
if l2 != nil {
sum += l2.Val
l2 = l2.Next
}
carry = sum / 10
curr.Next = &ListNode{Val: sum % 10}
curr = curr.Next
}
return dummy.Next
}
Java¶
// addTwoNumbers — Optimal: simultaneous traversal with carry
// Time: O(max(m,n)), Space: O(max(m,n))
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
int carry = 0;
while (l1 != null || l2 != null || carry != 0) {
int sum = carry;
if (l1 != null) { sum += l1.val; l1 = l1.next; }
if (l2 != null) { sum += l2.val; l2 = l2.next; }
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
}
return dummy.next;
}
Python¶
def addTwoNumbers(self, l1, l2):
"""
Optimal: simultaneous traversal with carry
Time: O(max(m,n)), Space: O(max(m,n))
"""
dummy = ListNode()
curr = dummy
carry = 0
while l1 or l2 or carry:
total = carry
if l1:
total += l1.val
l1 = l1.next
if l2:
total += l2.val
l2 = l2.next
carry, digit = divmod(total, 10)
curr.next = ListNode(digit)
curr = curr.next
return dummy.next
Dry Run¶
Input: l1 = [2,4,3], l2 = [5,6,4] (342 + 465)
dummy → ? carry = 0
Step 1: l1.val=2, l2.val=5
sum = 0 + 2 + 5 = 7
carry = 0, digit = 7
list: dummy → [7]
Step 2: l1.val=4, l2.val=6
sum = 0 + 4 + 6 = 10
carry = 1, digit = 0
list: dummy → [7] → [0]
Step 3: l1.val=3, l2.val=4
sum = 1 + 3 + 4 = 8
carry = 0, digit = 8
list: dummy → [7] → [0] → [8]
Both lists exhausted, carry = 0 → STOP
Return dummy.next = [7, 0, 8] ✅
Input: l1 = [9,9], l2 = [1] (99 + 1 = 100)
Step 1: sum = 0 + 9 + 1 = 10 → carry=1, digit=0
Step 2: sum = 1 + 9 + 0 = 10 → carry=1, digit=0 (l2 is exhausted, treated as 0)
Step 3: sum = 1 + 0 + 0 = 1 → carry=0, digit=1 (both exhausted, but carry=1 keeps loop going)
Result: [0, 0, 1] represents 100 ✅
Complexity Comparison¶
| # | Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|---|
| 1 | Brute Force (Reconstruct Numbers) | O(m+n) | O(max(m,n)) | Simple to understand | Integer overflow for long lists |
| 2 | Simultaneous Traversal with Carry | O(max(m,n)) | O(max(m,n)) | No overflow risk, handles any length | Slightly more logic to track carry |
Which solution to choose?¶
- In an interview: Approach 2 — demonstrates understanding of linked list traversal and carry propagation
- In production: Approach 2 — safe for arbitrary-precision numbers
- On Leetcode: Approach 2 — the canonical expected solution
- For learning: Approach 1 first (build intuition), then Approach 2
Edge Cases¶
| # | Case | Input | Expected Output | Reason |
|---|---|---|---|---|
| 1 | Both zeros | l1=[0], l2=[0] | [0] | 0 + 0 = 0 |
| 2 | Single digit, no carry | l1=[1], l2=[2] | [3] | 1 + 2 = 3 |
| 3 | Single digit, with carry | l1=[5], l2=[5] | [0,1] | 5 + 5 = 10 |
| 4 | Different lengths | l1=[9,9], l2=[1] | [0,0,1] | 99 + 1 = 100 |
| 5 | Final carry creates extra node | l1=[9,9,9], l2=[1] | [0,0,0,1] | 999 + 1 = 1000 |
| 6 | All nines, carry throughout | l1=[9,9,9,9,9,9,9], l2=[9,9,9,9] | [8,9,9,9,0,0,0,1] | Maximum carry propagation |
| 7 | l2 longer than l1 | l1=[5], l2=[8,7,6] | [3,8,6] | 5 + 678 = 683 |
Common Mistakes¶
Mistake 1: Forgetting the final carry¶
# ❌ WRONG — stops when both lists end, misses the last carry
while l1 or l2:
...
# ✅ CORRECT — keep going while carry is non-zero
while l1 or l2 or carry:
...
Example: [5] + [5] → sum=10, carry=1. After both lists end, the carry must become a new node [1]. Result should be [0, 1].
Mistake 2: Not handling different-length lists¶
# ❌ WRONG — crashes with AttributeError if l1 is None
sum += l1.val
l1 = l1.next
# ✅ CORRECT — guard with None check
if l1 is not None:
sum += l1.val
l1 = l1.next
Mistake 3: Modifying input pointers (using l1/l2 directly)¶
# This is actually fine — we advance l1/l2 to traverse,
# but the nodes themselves are not modified.
# The issue arises only if you try to reuse l1/l2 after calling the function.
Mistake 4: Off-by-one on dummy node¶
# ❌ WRONG — returns the dummy node itself (val=0 extra at front)
return dummy
# ✅ CORRECT — return the first real node
return dummy.next
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 445. Add Two Numbers II | 🟡 Medium | Same but digits stored in forward order — requires reversing or using a stack |
| 2 | 21. Merge Two Sorted Lists | 🟢 Easy | Two-list traversal with dummy head pattern |
| 3 | 415. Add Strings | 🟢 Easy | Same carry logic but on strings instead of linked lists |
| 4 | 67. Add Binary | 🟢 Easy | Same carry logic with binary digits |
| 5 | 2. Add Two Numbers (this) | 🟡 Medium | — |
Visual Animation¶
Interactive animation: animation.html
In the animation: - Step-by-step tab — visualizes both linked lists digit by digit with carry propagation highlighted - Dry Run tab — traces through Example 1 and Example 3 column by column - Compare tab — shows Brute Force vs Optimal side by side