31. Add Two Numbers
mediumAsked at PlaidAdd two numbers represented as reverse-order linked lists. Plaid asks this because adding two arbitrary-precision balances digit-by-digit with carry is exactly how their ledger code handles cents that overflow JS Number precision.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Plaid loops.
- Glassdoor (2025)— Plaid SWE II onsite — framed as ledger arbitrary-precision add.
- Blind (2026)— Plaid backend screen.
Problem
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.
Constraints
The number of nodes in each linked list is in the range [1, 100].0 <= Node.val <= 9It is guaranteed that the list represents a number that does not have leading zeros.
Examples
Example 1
l1 = [2,4,3], l2 = [5,6,4][7,0,8]Explanation: 342 + 465 = 807
Example 2
l1 = [0], l2 = [0][0]Approaches
1. Convert to numbers, add, convert back
Reverse each list into a string, BigInt-add, rebuild a list.
- Time
- O(n+m)
- Space
- O(n+m)
function addTwoNumbers(l1, l2) {
function toBig(l) {
let s = '';
for (let n = l; n; n = n.next) s = n.val + s;
return BigInt(s);
}
const sum = (toBig(l1) + toBig(l2)).toString();
let head = null;
for (const ch of sum) head = { val: +ch, next: head };
return head;
}Tradeoff: Dodges the carry-propagation algorithm. Plaid wants the digit-by-digit loop.
2. Digit-by-digit with carry
Walk both lists in lockstep, summing digit + digit + carry. Emit (sum % 10) as the next node; carry = (sum / 10) | 0.
- Time
- O(max(n, m))
- Space
- O(max(n, m))
function addTwoNumbers(l1, l2) {
const dummy = { next: null };
let cur = dummy, carry = 0;
while (l1 || l2 || carry) {
const a = l1 ? l1.val : 0;
const b = l2 ? l2.val : 0;
const sum = a + b + carry;
carry = (sum / 10) | 0;
cur.next = { val: sum % 10, next: null };
cur = cur.next;
if (l1) l1 = l1.next;
if (l2) l2 = l2.next;
}
return dummy.next;
}Tradeoff: Single pass, handles unequal lengths and final carry inside the same loop. The 'while (l1 || l2 || carry)' is the trick that unifies three cases.
Plaid-specific tips
Plaid loves this because it mirrors their ledger math — never store balances as JS Number (precision loss), always carry digit-by-digit. Bonus signal: explicitly mention 'I'm tracking carry separately because final overflow is a digit, not a discard.' That maps to fee+principal+interest summing in their billing pipeline.
Common mistakes
- Forgetting the trailing carry — sum of [9,9] + [1] is [0,0,1], not [0,0].
- Looping only while both l1 and l2 — drops the longer list.
- Using (sum / 10) without the | 0 floor — JS gives a float.
Follow-up questions
An interviewer at Plaid may pivot to one of these next:
- What if digits are stored in forward order (LC 445)? — reverse first or use a stack.
- Multiply two numbers represented as lists.
- Big-int add for two arrays of digits (LC 989).
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why digit-by-digit instead of BigInt?
BigInt works but obscures the algorithm. In production, ledger math is digit-by-digit (or cents-as-integers) because that's auditable and precision-safe.
Why the dummy head?
Removes the special case for the first node — without it you'd need an if-statement to set the head separately.
Practice these live with InterviewChamp.AI
Drill Add Two Numbers and other Plaid interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →