DSA sheet · Linked List · Stack / hash map pattern

Add Two Numbers

This video opens a new pattern in the sheet: linked list with a stack or a hash map. In this pattern we are allowed some extra space (a stack, a map, a list) if the logic needs it, though the extra space is not always part of the best answer. The first problem is the classic school addition, digit by digit with a carry, where each number is stored as a linked list. The teacher goes from "convert to integers" (rejected) → "copy digits into arrays" (works, extra space) → "add while walking both lists" (the optimal one). The "keep going while either list or the carry is left" loop is the heart of it.

Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember

Part 0 · Before starting

What is a linked list node?

A linked list is a chain of nodes. Each node holds a value (val) and a link to the next node (next). The first node is the head. The last node points to None, which means "the end".

given by LeetCode, don't write this in the solution
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val      # one digit (0-9) in this problem
        self.next = next    # the next node, or None at the end
l1 (head)
 ↓
[2] → [4] → [3] → None

Walking through a list

We use a pointer, for example l1 itself or curr, and move it with l1 = l1.next until it becomes None. A linked list has no index access: there is no l1[2]. To reach position i you walk i steps, which is O(n). That's why our solution walks the lists from the front, once.

Save next before changing a pointer

The next link is the only way to reach the rest of the list. If you overwrite it without keeping the old value, the rest is lost. Here we never rewire the input lists: we only read l1 and l2 and build a fresh answer list, so this danger doesn't come up. It's still the first thing to check whenever you write x.next = ….

The dummy node + tail pointer (for building a new list)

To build a new list one node at a time, we put a fake node in front: dummy = ListNode(0). A second pointer curr always stands on the last node (the tail). To add a node: curr.next = ListNode(d), then curr = curr.next. The dummy stays where it is, so at the end the real head is dummy.next. Without a dummy, the first node would be a special case ("if the answer is empty, this node becomes the head").

[0] → [7] → [0] → [8]
 ↑                 ↑
dummy             curr     → return dummy.next  (the 7)

School addition, carry, % and //

When we add 342 + 465 on paper, we start from the rightmost digit: 2 + 5 = 7. Then 4 + 6 = 10: write 0, carry 1 to the next column. Then 3 + 4 + 1 = 8. Answer 807.

Part A · Brute force: copy the digits into arrays

LeetCode 2 · Add Two Numbers

1The question in simple words

You get two non-empty linked lists. Each one is a non-negative number, but its digits are stored in reverse order: the head is the ones digit. Add the two numbers and return the sum as a linked list, also in reverse order. There are no leading zeros (except the number 0 itself).

l1:  [2] → [4] → [3] → None      means 342
l2:  [5] → [6] → [4] → None      means 465
                                 342 + 465 = 807
ans: [7] → [0] → [8] → None      (807 written backwards)

Why "reverse order" is a gift. For normal addition we need the last digits first. If the lists were in normal order (2 → 4 → 3 meaning 243), we would have to reverse them, or push the digits on a stack and pop from the end. The teacher shows that idea first (it's the reason this pattern mentions stacks). But here the question has already reversed the numbers for us: the head is the ones digit. So we can just add from the head, left to right, and that is exactly right-to-left on paper.

on paper:            the list holds it as:
    3 4 2                2 → 4 → 3
  + 4 6 5                5 → 6 → 4
  -------                ---------
    8 0 7                7 → 0 → 8     (read the columns right to left
                                        = read the lists left to right)

2What the constraints tell us

3Intuition: do it like on paper

Put one finger on each list. At each step, add the two digits under your fingers plus the carry. Write down the last digit, keep the carry, and move both fingers one step. If one number is shorter, its finger has fallen off the end: treat its digit as 0. When both fingers are off and there's no carry left, you're done.

4Building the logic from examples

Idea 1 (rejected): convert both lists to integers

The first thought: read 342 and 465 as integers, add them to get 807, then break 807 back into digits and build a list. The teacher rejects this because of the constraint: a list can have 100 nodes, so a number can have 100 digits. An int holds about 10 digits and a long about 19, so the number won't fit. If the lists were at most 15-18 digits, it could have worked.

Idea 1 (not accepted in an interview): integers
class Solution:
    def addTwoNumbers(self, l1, l2):
        def to_int(node):
            num, place = 0, 1
            while node:
                num += node.val * place    # head is the ones digit
                place *= 10
                node = node.next
            return num

        total = to_int(l1) + to_int(l2)
        dummy = ListNode(0)
        curr = dummy
        while True:
            curr.next = ListNode(total % 10)
            curr = curr.next
            total //= 10
            if total == 0:
                break
        return dummy.next
Doubt: Python ints never overflow, so doesn't this actually work in Python?
→ Yes, in Python it gives the right answer (and our tests check it). But it only works because Python quietly uses big-number arithmetic. In Java or C++ it overflows, and in an interview it dodges the real question, which is about doing the addition with the lists. Treat it as a "why not" idea, not the answer.

Idea 2 (the brute force): copy the digits into two arrays

Read l1 into an array a = [2, 4, 3] and l2 into b = [5, 6, 4]. Now index i walks a, index j walks b, and we keep a carry starting at 0.

Answer array: [7, 0, 8]. Then build a linked list from it with a dummy node.

How long can the answer be? max(n, m) + 1

The two lists don't have to be the same length. The teacher changes the example: l1 = 2 → 4 → 3, l2 = 5 → 6 → 4 → 5 → 6 (lengths 3 and 5). And she shows that a final carry can add one extra digit: e.g. if the last column is 5 + 4 + 1 = 10, we write 0 and still have carry 1, which becomes a new last digit 1. So the answer has at most max(n, m) + 1 digits.

The loop condition: i < n or j < m or carry

When should we stop? Not when i runs out, and not even when both run out:

So we keep looping while any of the three is still there, and that is why it's or, not and.

Doubt: why check if i < n again inside the loop?
→ Because the loop uses or. Being inside the loop only tells us that at least one of the three is alive, not which one. Reading a[i] when i is already past the end would crash. So each array gets its own bounds check before we read from it.
Doubt: why start s = carry instead of s = 0?
→ Just a shortcut. Starting with the carry already inside means we don't have to remember to add it later. Starting at 0 and writing s += carry is the same thing.
Doubt: do I need "if s > 9" before using % 10 and // 10?
→ No. The teacher first writes it with an if/else to make it clear, then points out that the two lines alone are enough: for s = 8, 8 % 10 = 8 (the digit unchanged) and 8 // 10 = 0 (no carry). So the same two lines handle both cases.

5Approach steps

  1. Copy l1's digits into a and l2's digits into b.
  2. Set i = j = carry = 0, and an empty answer array.
  3. While i < len(a) or j < len(b) or carry: s = carry + (a[i] if it exists) + (b[j] if it exists). Append s % 10, set carry = s // 10, and move i and j.
  4. Build a linked list from the answer array with a dummy node, and return dummy.next.

6Code (Python)

Brute force: arrays + carry
class Solution:
    def addTwoNumbers(self, l1, l2):
        a, b = [], []
        while l1:                        # copy digits of l1
            a.append(l1.val)
            l1 = l1.next
        while l2:                        # copy digits of l2
            b.append(l2.val)
            l2 = l2.next

        ans = []
        i = j = carry = 0
        while i < len(a) or j < len(b) or carry != 0:
            s = carry
            if i < len(a):
                s += a[i]
                i += 1
            if j < len(b):
                s += b[j]
                j += 1
            ans.append(s % 10)           # the digit to write
            carry = s // 10              # 0 or 1

        dummy = ListNode(0)              # build the answer list
        curr = dummy
        for d in ans:
            curr.next = ListNode(d)
            curr = curr.next
        return dummy.next

7Code line by line

linewhat it means
while l1: a.append(l1.val)Read every digit of l1 into a normal array. Same for l2. This is the extra space.
i = j = carry = 0Two indices, one per array, and no carry at the start.
while i < len(a) or j < len(b) or carry != 0:Keep going while there is any digit or any carry left.
s = carryStart the column sum with the carry from the previous column.
if i < len(a): s += a[i]; i += 1Add a's digit only if a still has one. A missing digit counts as 0.
if j < len(b): …Same for b. A separate if, not elif, because both can be present.
ans.append(s % 10)The digit for this column.
carry = s // 10What moves to the next column (0 or 1 here).
dummy … curr.next = ListNode(d)Turn the digit array into a linked list. Return dummy.next, the real head.

8Dry run: different lengths with a final carry

l1 = 9 → 9 (means 99), l2 = 1 (means 1). 99 + 1 = 100, so the answer should be 0 → 0 → 1. a = [9, 9], b = [1].

stepi, j, carry insdigit (s % 10)carry outans so far
10, 0, 00 + 9 + 1 = 1001[0]
21, 1 (b ended), 11 + 9 = 1001[0, 0]
32 (a ended), 1 (b ended), 1110[0, 0, 1]
stopall three are donebuild 0 → 0 → 1

Step 3 only runs because of the or carry part of the condition. Without it, we would lose the leading 1 and return 0 → 0 (which means 0, wrong).

9Complexity & remember

Remember the brute forceCopy digits to arrays, then add column by column with i, j and carry, looping while i or j or carry is left. It works, but it stores the values outside the list, which linked list interviews want you to avoid.

Part B · Optimal: add while walking both lists

1The question (same as Part A)

Same input and output. The new rule: don't copy the digits into arrays first. Work directly on the nodes.

2What the constraints tell us

Same as Part A: at least 1 node each, so no empty-list base case is needed. Digits 0-9, so the carry is 0 or 1. Up to 100 nodes, so no integer conversion.

3Intuition: we were already walking both lists

The teacher's key observation: in the brute force, we already walked l1 and l2 just to copy the digits. Both walks started at the head, at the same time. So why not add the digits during that walk, instead of saving them first? While walking, we add the two current digits + carry, and attach a new node to a growing answer list.

4Building the logic

Step 1: the answer list starts with a dummy

dummy = ListNode(0), curr = dummy. Each new digit is attached at curr.next, then curr moves forward.

Step 2: don't write l1.val + l2.val directly

Adding l1.val + l2.val only works while both lists still have nodes. When one ends, it's None, and None.val crashes. Handling that separately would need extra loops ("while only l1 is left, keep adding carry + l1.val …", and the same for l2). The teacher avoids all of that:

A list that has ended just adds nothing (like a 0 digit).

Doubt: why two separate ifs and not if … elif?
→ With elif, only one of the two would run. When both lists have a digit, we must add both. The teacher points this out while typing the code.

Step 3: write the digit, keep the carry

carry = s // 10, then curr.next = ListNode(s % 10), then curr = curr.next. As in Part A, these two operations work for both small and large sums, so no if/else is needed.

Step 4: the same loop condition

while l1 or l2 or carry: keep going while either list has nodes left, or a carry is still waiting to be written.

Step 5: return dummy.next

When the loop ends, curr is on the last digit, and the dummy is in front of the first one. The real head (the 7 in our example) is dummy.next.

5Approach steps

  1. dummy = ListNode(0), curr = dummy, carry = 0.
  2. While l1 or l2 or carry: s = carry; add and advance l1 if present; add and advance l2 if present.
  3. carry = s // 10; attach ListNode(s % 10); move curr.
  4. Return dummy.next.

6Code (Python)

Optimal: one walk, dummy + carry
class Solution:
    def addTwoNumbers(self, l1, l2):
        dummy = ListNode(0)              # fake head of the answer
        curr = dummy                     # tail of the answer
        carry = 0

        while l1 is not None or l2 is not None or carry != 0:
            s = carry
            if l1 is not None:
                s += l1.val
                l1 = l1.next
            if l2 is not None:           # separate if: both may add
                s += l2.val
                l2 = l2.next
            carry = s // 10
            curr.next = ListNode(s % 10)
            curr = curr.next

        return dummy.next

7Code line by line

linewhat it means
dummy = ListNode(0) curr = dummyFake head for the answer, and a tail pointer that will move.
carry = 0No carry before the first column.
while l1 is not None or l2 is not None or carry != 0:Any digit left in either list, or a carry left → one more column.
s = carryThe column sum starts with the carry.
if l1 is not None: s += l1.val l1 = l1.nextUse l1's digit if it has one, and step l1 forward. If l1 has ended, it adds nothing.
if l2 is not None: …Same for l2.
carry = s // 10The carry for the next column (0 or 1).
curr.next = ListNode(s % 10) curr = curr.nextAttach the new digit to the end of the answer, and move the tail onto it.
return dummy.nextSkip the fake 0. This is the ones digit of the answer.

8Dry run

Example 1: l1 = 2 → 4 → 3, l2 = 5 → 6 → 4

stepl1, l2 (values)scarry outwhat changesanswer list after
12, 50 + 2 + 5 = 70curr.next = [7]; l1 → 4, l2 → 60 → 7
24, 60 + 4 + 6 = 101curr.next = [0]; l1 → 3, l2 → 40 → 7 → 0
33, 41 + 3 + 4 = 80curr.next = [8]; l1, l2 → None0 → 7 → 0 → 8
stopNone, Nonecarry 0 too → loop ends → return dummy.next = 7 → 0 → 8 (807)

Snapshot after step 2:

l1:  [2] → [4] → [3] → None          l2:  [5] → [6] → [4] → None
                  ↑                                      ↑
                  l1                                     l2
ans: [0] → [7] → [0]          carry = 1
      ↑           ↑
    dummy        curr

Example 2: different lengths + extra carry (the teacher's variant)

l1 = 2 → 4 → 3 (342), l2 = 5 → 6 → 4 → 5 → 6 (65465). Sum = 65807.

stepl1, l2sdigitcarryanswer so far
12, 57707
24, 610017 → 0
33, 48807 → 0 → 8
4None, 55507 → 0 → 8 → 5
5None, 66607 → 0 → 8 → 5 → 6

l1 ran out after step 3, but the loop kept going because l2 was still alive. The answer has max(3, 5) = 5 digits. With l1 = 9 → 9 and l2 = 1, a 3rd step runs only for the carry and adds a new node 1, giving max(2, 1) + 1 = 3 digits.

(While explaining, the teacher misspeaks once and calls 2 + 5 "nine", and later says "greater than 12, modulo gives 2". The ideas are right: 2 + 5 = 7, and a sum of 12 gives digit 2 and carry 1.)

9Complexity & remember

Remember Add Two NumbersReverse order = add from the head. while l1 or l2 or carry: s = carry + whatever digits exist (two separate ifs), write s % 10, carry s // 10, return dummy.next.

Part C · Revision page

Convert to intArrays (Part A)One walk (Part B)
ideabuild the numbers, add, split backcopy digits, add with i, j, carryadd while walking, build answer on the fly
works for 100 digits?no in Java/C++ (overflow); yes in Python only thanks to big intsyesyes
timeO(n + m)O(n + m)O(max(n, m))
extra spacebig numbersO(n + m) arrays + answeronly the answer list
loop stops whennumber becomes 0i, j and carry all donel1, l2 and carry all done
If you remember only 5 lines 1. Digits are stored reversed, so the head is the ones digit: just add from the head.
2. Digits 0-9 → the carry is always 0 or 1.
3. Loop while l1 or l2 or carry; a missing digit counts as 0.
4. s % 10 is the digit, s // 10 is the carry; no if/else needed.
5. Build with a dummy + tail; return dummy.next.
Mistakes to avoid ✗ stopping when both lists end but a carry is still 1 (you lose the last digit)
✗ reading l1.val when l1 is None
✗ using elif for l2 (only one digit gets added)
✗ converting to int (overflows for 100 digits outside Python)
✗ returning dummy instead of dummy.next
test it yourself (paste under any solution)
def build(vals):
    dummy = ListNode(0)
    t = dummy
    for v in vals:
        t.next = ListNode(v)
        t = t.next
    return dummy.next

def to_list(head):
    out = []
    while head:
        out.append(head.val)
        head = head.next
    return out

s = Solution()
print(to_list(s.addTwoNumbers(build([2, 4, 3]), build([5, 6, 4]))))   # [7, 0, 8]
print(to_list(s.addTwoNumbers(build([0]), build([0]))))               # [0]
print(to_list(s.addTwoNumbers(build([9, 9]), build([1]))))            # [0, 0, 1]

Based on this video: Add Two Numbers