Add two numbers stored in forward-order linked lists using stacks and carry propagation.
Problem Restatement
We are given two non-empty linked lists.
Each linked list represents a non-negative integer.
Each node stores one digit, and the digits are stored in forward order. That means the most significant digit comes first.
For example:
7 -> 2 -> 4 -> 3represents:
7243We need to add the two numbers and return the sum as a linked list, also in forward order.
The numbers have no leading zero, except for the number 0 itself. The official problem asks us to return the sum as a linked list with the most significant digit first.
Input and Output
| Item | Meaning |
|---|---|
| Input | Two linked lists l1 and l2 |
| Output | A linked list representing their sum |
| Digit order | Most significant digit first |
| Node value | A single digit from 0 to 9 |
| Leading zeros | Not present, except the number 0 |
The node class is usually:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = nextExamples
Example 1:
l1 = [7, 2, 4, 3]
l2 = [5, 6, 4]These represent:
7243 + 564 = 7807So the output is:
[7, 8, 0, 7]Example 2:
l1 = [2, 4, 3]
l2 = [5, 6, 4]These represent:
243 + 564 = 807Output:
[8, 0, 7]Example 3:
l1 = [0]
l2 = [0]Output:
[0]First Thought: Reverse the Lists
If the digits were stored backward, addition would be easy.
We could start from the ones place, add digit by digit, and carry as needed.
But here the lists begin with the largest place value.
One option is:
- Reverse both input lists.
- Add them normally.
- Reverse the result.
This works, but it modifies the input lists unless we carefully copy them first.
A cleaner solution is to use stacks.
Key Insight
A stack lets us read the linked list from left to right, then process it from right to left.
For example:
l1 = 7 -> 2 -> 4 -> 3After pushing values into a stack:
[7, 2, 4, 3]Popping gives:
3, 4, 2, 7That is exactly the order needed for manual addition.
We use two stacks:
| Stack | Stores |
|---|---|
stack1 | Digits from l1 |
stack2 | Digits from l2 |
Then we repeatedly pop digits, add them with carry, and build the result by inserting each new digit at the front.
Algorithm
Push all digits from l1 into stack1.
Push all digits from l2 into stack2.
Initialize:
carry = 0
head = NoneWhile either stack still has digits, or carry is non-zero:
- Start with
total = carry. - If
stack1is not empty, pop and add its top digit. - If
stack2is not empty, pop and add its top digit. - The new digit is:
total % 10- The new carry is:
total // 10- Create a new node for the digit.
- Insert it before the current result head.
Return head.
Correctness
The stacks store the digits of each input number in reverse processing order. Therefore, each pop gives the next digit from least significant to most significant.
At each step, the algorithm adds the current digit from l1, the current digit from l2, and the carry from the previous lower-place addition. This is exactly the standard column addition rule.
The digit stored in the result node is total % 10, which is the correct digit for the current place. The carry becomes total // 10, which is the amount that must be added to the next higher place.
Because we process digits from least significant to most significant, each new result digit belongs before the already-built result list. Inserting each new node at the front keeps the final linked list in forward order.
When both stacks are empty and carry is zero, all digits and carries have been processed. The returned list therefore represents the exact sum of the two input numbers.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(m + n) | Each input node is pushed and popped once |
| Space | O(m + n) | The stacks store all input digits |
Here, m and n are the lengths of the two linked lists.
The output list is not counted as extra working space.
Implementation
class Solution:
def addTwoNumbers(
self,
l1: 'Optional[ListNode]',
l2: 'Optional[ListNode]',
) -> 'Optional[ListNode]':
stack1 = []
stack2 = []
while l1:
stack1.append(l1.val)
l1 = l1.next
while l2:
stack2.append(l2.val)
l2 = l2.next
carry = 0
head = None
while stack1 or stack2 or carry:
total = carry
if stack1:
total += stack1.pop()
if stack2:
total += stack2.pop()
carry = total // 10
digit = total % 10
node = ListNode(digit)
node.next = head
head = node
return headCode Explanation
First, we collect the digits:
stack1 = []
stack2 = []Each linked list is scanned from head to tail.
while l1:
stack1.append(l1.val)
l1 = l1.nextAfter this, the top of the stack is the least significant digit.
The addition loop continues while there is still work:
while stack1 or stack2 or carry:We begin each column with the previous carry:
total = carryThen we add one digit from each stack if available:
if stack1:
total += stack1.pop()
if stack2:
total += stack2.pop()The carry and output digit are computed by:
carry = total // 10
digit = total % 10The new node is inserted at the front:
node = ListNode(digit)
node.next = head
head = nodeThis keeps the result in most-significant-first order.
Testing
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
dummy = ListNode()
curr = dummy
for value in values:
curr.next = ListNode(value)
curr = curr.next
return dummy.next
def to_list(head):
values = []
while head:
values.append(head.val)
head = head.next
return values
def run_tests():
s = Solution()
assert to_list(
s.addTwoNumbers(
build_list([7, 2, 4, 3]),
build_list([5, 6, 4]),
)
) == [7, 8, 0, 7]
assert to_list(
s.addTwoNumbers(
build_list([2, 4, 3]),
build_list([5, 6, 4]),
)
) == [8, 0, 7]
assert to_list(
s.addTwoNumbers(
build_list([0]),
build_list([0]),
)
) == [0]
assert to_list(
s.addTwoNumbers(
build_list([9, 9, 9]),
build_list([1]),
)
) == [1, 0, 0, 0]
assert to_list(
s.addTwoNumbers(
build_list([1]),
build_list([9, 9, 9]),
)
) == [1, 0, 0, 0]
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[7,2,4,3] + [5,6,4] | Checks standard example |
| Equal length lists | Checks ordinary carry handling |
| Both zero | Checks smallest value |
| Final carry | Checks creation of a new leading digit |
| Unequal lengths | Checks missing digits are treated as zero |