Add 2 Numbers in Linked List | Dummy Node Approach
Adding two numbers represented by linked lists is a classic data structures and algorithms interview problem. This chapter covers how to efficiently add numbers stored in reverse order within singly linked lists, utilizing the clean and elegant Dummy Node Approach to handle result list construction without messy edge-case head initialization logic.
1. Why Do We Need This? (Problem Statement & Status Quo)
You are given two non-empty linked lists, L1 and L2, representing two non-negative integers. The digits are stored in reverse order, and each node contains a single digit.
- The Challenge: Add the two numbers and return the sum as a linked list in the exact same reverse order.
- Naive Approach Pitfalls: Without a structured design pattern, keeping track of the head node of the new result list requires complex conditional checks (e.g., checking if it's the very first node being created, managing initial pointer assignments, and handling trailing carryovers). This leads to bloated, error-prone code filled with null pointer checks.
Example 1: Basic Addition with Carry
L1:6 -> 4 -> 2(Represents number246)L2:7 -> 8 -> 3(Represents number387)- Mathematical Sum:
- Result Linked List (Reverse Order):
3 -> 3 -> 6
2. Core Mental Model: Real-World Arithmetic Analogy
Real-Life Analogy: Elementary School Addition on Paper
Think about how you add two numbers on a sheet of paper by hand:
- You always align the numbers vertically.
- You start from the rightmost column (the units place).
- You add the digits together, plus any incoming
carryfrom the previous column. - If the sum is 10 or greater, you write down the units digit (e.g.,
sum % 10) and carry over the tens digit (sum / 10) to the next column on the left. - You repeat this moving left until all digits and final carries are exhausted.
Because our linked lists store digits in reverse order, the head node of each linked list naturally represents the units place. This allows us to traverse both lists simultaneously from left to right (head to tail), exactly mimicking manual addition from units to tens to hundreds.
3. The Dummy Node Strategy
To avoid writing messy conditional checks for whether we are creating the first node of our result list, we introduce a Dummy Node.
- What is a Dummy Node? A temporary sentinel node (initialized with a placeholder value like
-1) placed at the very beginning of our result list. - Why use it? A
currentpointer starts at the dummy node. Whenever we compute a new sum digit, we attach a new node tocurrent.nextand advancecurrent. - The Final Result: Once the traversal and carry processing complete, our true result list head is simply
dummyHead.next.
4. Step-by-Step Execution Walkthrough
Let's trace an execution with L1 = [5, 3] (representing 35) and L2 = [4, 5, 9, 9] (representing 9954).
- Mathematical Sum:
- Expected Result List:
[9, 8, 9, 9]
Step Initialization
Iteration Breakdown
Loop terminates because both T1 and T2 have reached null and carry is 0.
5. Complete Algorithm Pseudo-Code
Here is the clean, robust implementation of the addition algorithm using the dummy node pattern:
class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = (val===undefined ? 0 : val);
this.next = (next===undefined ? null : next);
}
}
function addTwoNumbers(l1: ListNode | null, l2: ListNode | null): ListNode | null {
let dummyHead = new ListNode(-1);
let cur = dummyHead;
let t1 = l1;
let t2 = l2;
let carry = 0;
// Continue loop if either list has remaining nodes or if there is a leftover carry
while (t1 !== null || t2 !== null || carry !== 0) {
let sum = carry;
if (t1 !== null) {
sum += t1.val;
t1 = t1.next;
}
if (t2 !== null) {
sum += t2.val;
t2 = t2.next;
}
carry = Math.floor(sum / 10);
let newNode = new ListNode(sum % 10);
cur.next = newNode;
cur = cur.next;
}
return dummyHead.next;
}
6. Real-World Practical Pitfalls & Edge Cases
When writing or debugging this code in production or during interviews, watch out for these common failure modes:
- Null Pointer Exceptions (
NullPointerException/TypeError):- Pitfall: Directly accessing
t1.valort1.nextwithout checking ift1has becomenull. - Fix: Always wrap list traversal checks in conditional statements (
if (t1 !== null)).
- Pitfall: Directly accessing
- Leftover Carry After Traversal:
- Pitfall: Stopping the loop the exact moment
t1andt2hitnull, missing a final carry (e.g., , where a1carry remains after both lists are exhausted). - Fix: Include
carry !== 0in yourwhileloop condition (while (t1 !== null || t2 !== null || carry !== 0)). This ensures a final node is automatically created for the leftover carry.
- Pitfall: Stopping the loop the exact moment
- Lists of Unequal Length:
- Pitfall: Assuming
L1andL2have the exact same number of nodes. - Fix: The algorithm independently checks
t1andt2. Once a list is exhausted, its value contribution is treated as0while the longer list continues to process.
- Pitfall: Assuming
7. Complexity Analysis
Note: The space utilized by the result list is strictly required by the problem statement to return the output, meaning auxiliary algorithmic overhead beyond the output structure remains at .
Summary and Key Takeaways
- Reverse-Order Alignment: Because lists are given in reverse order, the list head naturally corresponds to the units place, simplifying addition.
- Elementary Math Simulation: Replicate manual addition by tracking
sum, extracting the digit viasum % 10, and forwarding the carry viasum / 10. - Dummy Node Utility: Use a dummy sentinel node to anchor the result list, eliminating messy edge cases when initializing the head pointer.
- Exhaustive Loop Condition: Ensure your traversal loop continues as long as
t1 !== null,t2 !== null, orcarry !== 0. - Null Safety: Always check pointer validity before advancing
.nextto prevent runtime crashes.