Files
codetutor/backend/data/questions/add-two-numbers-ii.yaml

246 lines
9.7 KiB
YAML

title: Add Two Numbers II
slug: add-two-numbers-ii
difficulty: medium
leetcode_id: 445
leetcode_url: https://leetcode.com/problems/add-two-numbers-ii/
categories:
- linked-lists
- stack
- math
patterns:
- slug: linkedlist-reversal
is_optimal: true
function_signature: "def add_two_numbers(l1: ListNode, l2: ListNode) -> ListNode:"
test_cases:
visible:
- input: { l1: [7, 2, 4, 3], l2: [5, 6, 4] }
expected: [7, 8, 0, 7]
- input: { l1: [2, 4, 3], l2: [5, 6, 4] }
expected: [8, 0, 7]
- input: { l1: [0], l2: [0] }
expected: [0]
hidden:
- input: { l1: [9, 9, 9], l2: [1] }
expected: [1, 0, 0, 0]
- input: { l1: [5], l2: [5] }
expected: [1, 0]
- input: { l1: [1, 2, 3, 4, 5], l2: [6, 7, 8, 9] }
expected: [1, 9, 1, 3, 4]
- input: { l1: [9, 9, 9, 9, 9], l2: [9, 9, 9, 9, 9] }
expected: [1, 9, 9, 9, 9, 8]
- input: { l1: [1], l2: [9, 9, 9, 9] }
expected: [1, 0, 0, 0, 0]
description: |
You are given two **non-empty** linked lists representing two non-negative integers. The most significant digit comes first 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.
constraints: |
- `1 <= number of nodes in each list <= 100`
- `0 <= Node.val <= 9`
- The list represents a number without leading zeros
examples:
- input: "l1 = [7,2,4,3], l2 = [5,6,4]"
output: "[7,8,0,7]"
explanation: "7243 + 564 = 7807"
- input: "l1 = [2,4,3], l2 = [5,6,4]"
output: "[8,0,7]"
explanation: "243 + 564 = 807"
- input: "l1 = [0], l2 = [0]"
output: "[0]"
explanation: "0 + 0 = 0"
explanation:
intuition: |
Think of this problem like adding two numbers by hand on paper. When you add numbers manually, you start from the **rightmost digit** (the least significant) and work your way left, carrying over when a column sums to 10 or more.
The challenge here is that linked lists give us digits in the **opposite order** — most significant digit first. It's like being handed two numbers written left-to-right and being asked to add them without being able to peek at the end.
There are two main ways to handle this:
1. **Reverse the lists** — Convert the problem into "Add Two Numbers" (LeetCode 2), where digits are already in least-significant-first order. Add them, then reverse the result.
2. **Use stacks** — Push all digits onto stacks, then pop them off (which gives us least-significant-first order naturally). This avoids modifying the input lists.
Both approaches transform the problem into one where we process digits from right to left, just like manual addition.
approach: |
We'll use the **Stack Approach** to solve this without modifying the input lists:
**Step 1: Push all digits onto stacks**
- Create two stacks, one for each linked list
- Traverse each list and push every value onto its respective stack
- After this step, the top of each stack holds the least significant digit
&nbsp;
**Step 2: Add digits with carry**
- Initialise `carry` to `0` and `result` pointer to `None`
- While either stack has elements OR there's a carry:
- Pop from stack 1 if not empty, otherwise use `0`
- Pop from stack 2 if not empty, otherwise use `0`
- Calculate `total = val1 + val2 + carry`
- The digit for this position is `total % 10`
- The new carry is `total // 10`
- Create a new node with this digit and prepend it to the result
&nbsp;
**Step 3: Build result by prepending**
- Each new node becomes the new head of our result list
- This naturally builds the list in most-significant-first order
- Return the head of the result list
&nbsp;
This approach mirrors how we add numbers by hand, processing from right to left while propagating carries.
common_pitfalls:
- title: Forgetting the Final Carry
description: |
After processing all digits from both numbers, there might still be a carry left over. For example, adding `[9,9]` and `[1]` gives `99 + 1 = 100`.
If you exit the loop when both stacks are empty but don't check for a remaining carry, you'll return `[0,0]` instead of `[1,0,0]`.
Always continue the loop while `carry > 0`, or handle the final carry after the main loop.
wrong_approach: "Exit loop when both stacks are empty"
correct_approach: "Continue while stacks have elements OR carry is non-zero"
- title: Building the Result List Backwards
description: |
Since we process digits from least significant to most significant, we need to build the result list in reverse order. If you append nodes to the end, you'll get the digits reversed.
For example, adding `[2,4,3]` and `[5,6,4]`:
- Process: 3+4=7, 4+6=10 (carry 1), 2+5+1=8
- Appending gives: `[7,0,8]` (wrong!)
- Prepending gives: `[8,0,7]` (correct!)
wrong_approach: "Append new nodes to the tail of result"
correct_approach: "Prepend new nodes to the head of result"
- title: Modifying Input Lists
description: |
While reversing the input lists works, the problem may expect you to leave them unchanged (especially in production code where the lists might be used elsewhere).
The stack approach naturally avoids this issue since we only read from the lists, never modify them.
If you do use the reversal approach, consider reversing the lists back to their original state before returning.
wrong_approach: "Reverse input lists in place without restoring"
correct_approach: "Use stacks, or reverse and restore the input lists"
key_takeaways:
- "**Stacks reverse order naturally**: When processing order matters, stacks can transform first-to-last into last-to-first without modifying the original data"
- "**Prepending builds lists in reverse**: To construct a list from least to most significant, prepend each new node as the head"
- "**Handle carries beyond input length**: The result can have more digits than either input (e.g., `999 + 1 = 1000`)"
- "**Related problem**: 'Add Two Numbers' (LeetCode 2) is the reverse version where least significant digit comes first"
time_complexity: "O(n + m). We traverse both lists once to fill the stacks, then process at most `max(n, m) + 1` digits during addition."
space_complexity: "O(n + m). The stacks store all digits from both input lists, plus the result list of similar size."
solutions:
- approach_name: Stack-Based Addition
is_optimal: true
code: |
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def add_two_numbers(l1: ListNode, l2: ListNode) -> ListNode:
# Push all digits onto stacks
stack1, stack2 = [], []
while l1:
stack1.append(l1.val)
l1 = l1.next
while l2:
stack2.append(l2.val)
l2 = l2.next
carry = 0
result = None
# Process digits from least to most significant
while stack1 or stack2 or carry:
# Get digits (0 if stack is empty)
val1 = stack1.pop() if stack1 else 0
val2 = stack2.pop() if stack2 else 0
# Add with carry
total = val1 + val2 + carry
digit = total % 10
carry = total // 10
# Prepend new node to result
new_node = ListNode(digit)
new_node.next = result
result = new_node
return result
explanation: |
**Time Complexity:** O(n + m) — We traverse each list once to build stacks, then process each digit once.
**Space Complexity:** O(n + m) — Stacks hold all digits from both lists.
The stack approach elegantly handles the digit ordering problem without modifying input lists. By popping from stacks, we naturally get digits in right-to-left order for addition.
- approach_name: Reverse Lists
is_optimal: false
code: |
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def add_two_numbers(l1: ListNode, l2: ListNode) -> ListNode:
def reverse(head):
"""Reverse a linked list in place."""
prev = None
while head:
next_node = head.next
head.next = prev
prev = head
head = next_node
return prev
# Reverse both lists to get least significant digit first
l1 = reverse(l1)
l2 = reverse(l2)
carry = 0
dummy = ListNode(0)
current = dummy
# Add digits from least to most significant
while l1 or l2 or carry:
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
total = val1 + val2 + carry
digit = total % 10
carry = total // 10
current.next = ListNode(digit)
current = current.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
# Reverse result to get most significant digit first
return reverse(dummy.next)
explanation: |
**Time Complexity:** O(n + m) — Three passes: reverse l1, reverse l2, reverse result.
**Space Complexity:** O(1) extra — Only uses pointers (result list doesn't count as extra space).
This approach converts the problem to the simpler "Add Two Numbers" variant by reversing the inputs. The tradeoff is that it modifies the input lists (which may or may not be acceptable depending on requirements).