title: Add Two Numbers
slug: add-two-numbers
difficulty: medium
leetcode_id: 2
leetcode_url: https://leetcode.com/problems/add-two-numbers/
categories:
  - linked-lists
  - math
patterns:
  - two-pointers

function_signature: "def add_two_numbers(l1: ListNode | None, l2: ListNode | None) -> ListNode | None:"

test_cases:
  visible:
    - input: { l1: [2, 4, 3], l2: [5, 6, 4] }
      expected: [7, 0, 8]
    - input: { l1: [0], l2: [0] }
      expected: [0]
    - input: { l1: [9, 9, 9, 9, 9, 9, 9], l2: [9, 9, 9, 9] }
      expected: [8, 9, 9, 9, 0, 0, 0, 1]
  hidden:
    - input: { l1: [1], l2: [9, 9] }
      expected: [0, 0, 1]
    - input: { l1: [5], l2: [5] }
      expected: [0, 1]
    - input: { l1: [9], l2: [1] }
      expected: [0, 1]
    - input: { l1: [2, 4, 9], l2: [5, 6, 4, 9] }
      expected: [7, 0, 4, 0, 1]
    - input: { l1: [1, 8], l2: [0] }
      expected: [1, 8]
    - input: { l1: [9, 9, 9], l2: [1] }
      expected: [0, 0, 0, 1]
    - input: { l1: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], l2: [5, 6, 4] }
      expected: [6, 6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

description: |
  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 <= 9`
  - It is guaranteed that the list represents a number that does not have leading zeros

examples:
  - input: "l1 = [2,4,3], l2 = [5,6,4]"
    output: "[7,0,8]"
    explanation: "342 + 465 = 807. Digits are stored in reverse order."
  - input: "l1 = [0], l2 = [0]"
    output: "[0]"
    explanation: "0 + 0 = 0"
  - input: "l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]"
    output: "[8,9,9,9,0,0,0,1]"
    explanation: "9999999 + 9999 = 10009998"

explanation:
  intuition: |
    The digits are stored in **reverse order**, which is actually perfect for addition! Think about how you add numbers by hand: you start from the rightmost digit (ones place), add, carry the overflow, and move left. Here, the "rightmost" digit is conveniently at the head of the list.

    Think of it like this: we're simulating elementary school addition, digit by digit:
    1. Add corresponding digits from both numbers
    2. If the sum is ≥ 10, keep the ones digit and carry 1 to the next position
    3. Move to the next digit and repeat

    The key insight is that we process both lists in parallel, handling cases where:
    - One list is longer than the other (treat missing digits as 0)
    - There's a carry after processing both lists (add an extra digit)

  approach: |
    We solve this using **Iterative Digit-by-Digit Addition**:

    **Step 1: Initialise the result list and carry**

    - Create a `dummy` head node (simplifies result construction)
    - Set `current` to point to `dummy`
    - Set `carry = 0`

    &nbsp;

    **Step 2: Process digits from both lists**

    - While `l1` is not None OR `l2` is not None OR `carry` is non-zero:
      - Get `val1 = l1.val if l1 else 0`
      - Get `val2 = l2.val if l2 else 0`
      - Compute `total = val1 + val2 + carry`
      - Create new node with `total % 10` (ones digit)
      - Update `carry = total // 10` (tens digit, if any)
      - Advance `l1` and `l2` if they have more nodes

    &nbsp;

    **Step 3: Return the result**

    - Return `dummy.next` (skip the dummy head)

    &nbsp;

    The condition `l1 or l2 or carry` ensures we handle all cases: unequal lengths and final carry.

  common_pitfalls:
    - title: Forgetting the Final Carry
      description: |
        After both lists are exhausted, there might still be a carry. For example, 99 + 1 = 100 — the final carry creates an extra digit.

        The loop condition `while l1 or l2 or carry` handles this by continuing as long as there's a carry to process.
      wrong_approach: "while l1 or l2 — stops even if carry is 1"
      correct_approach: "while l1 or l2 or carry"

    - title: Assuming Equal Length Lists
      description: |
        The two lists can have different lengths. When one list is exhausted, treat its "digits" as 0.

        Use `val1 = l1.val if l1 else 0` to handle this gracefully.
      wrong_approach: "while l1 and l2 — stops when either is exhausted"
      correct_approach: "Use 0 for missing digits: val = node.val if node else 0"

    - title: Converting to Integers
      description: |
        Don't convert the lists to integers, add them, and convert back. The numbers can have up to 100 digits, far exceeding standard integer limits in many languages.

        Process digit by digit to avoid overflow issues.
      wrong_approach: "num1 = int(digits1); result = num1 + num2"
      correct_approach: "Add digit by digit with carry"

  key_takeaways:
    - "**Dummy head simplifies linked list construction**: No special case for the first node"
    - "**Digit-by-digit avoids overflow**: Works with arbitrarily large numbers"
    - "**Handle unequal lengths gracefully**: Treat missing nodes as 0"
    - "**Don't forget the final carry**: The loop condition should include `or carry`"

  time_complexity: "O(max(m, n)). We traverse both lists once, processing max(m, n) digits where m and n are the list lengths."
  space_complexity: "O(max(m, n)). The result list has at most max(m, n) + 1 nodes (extra node for carry)."

solutions:
  - approach_name: Iterative with Carry
    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 | None, l2: ListNode | None) -> ListNode | None:
          dummy = ListNode()  # Dummy head for easy result construction
          current = dummy
          carry = 0

          # Continue while there are digits to process or carry to add
          while l1 or l2 or carry:
              # Get current digits (0 if list is exhausted)
              val1 = l1.val if l1 else 0
              val2 = l2.val if l2 else 0

              # Add digits and carry
              total = val1 + val2 + carry
              carry = total // 10  # Carry for next position
              digit = total % 10   # Current digit

              # Create new node and advance
              current.next = ListNode(digit)
              current = current.next

              # Advance input pointers if possible
              l1 = l1.next if l1 else None
              l2 = l2.next if l2 else None

          return dummy.next
    explanation: |
      **Time Complexity:** O(max(m, n)) — Single pass through both lists.

      **Space Complexity:** O(max(m, n)) — Result list size.

      We simulate manual addition: add corresponding digits plus carry, store the ones digit, propagate the carry. The dummy head eliminates special handling for the first node. The loop continues until both lists are exhausted AND there's no remaining carry.

  - approach_name: Recursive
    is_optimal: false
    code: |
      def add_two_numbers(
          l1: ListNode | None,
          l2: ListNode | None,
          carry: int = 0
      ) -> ListNode | None:
          # Base case: both lists empty and no carry
          if not l1 and not l2 and not carry:
              return None

          # Get current digit values
          val1 = l1.val if l1 else 0
          val2 = l2.val if l2 else 0

          # Calculate sum and create node
          total = val1 + val2 + carry
          node = ListNode(total % 10)

          # Recurse for remaining digits
          node.next = add_two_numbers(
              l1.next if l1 else None,
              l2.next if l2 else None,
              total // 10
          )

          return node
    explanation: |
      **Time Complexity:** O(max(m, n)) — Same as iterative.

      **Space Complexity:** O(max(m, n)) — Recursion stack depth.

      Recursive version processes one digit per call. Each call creates a node and recursively handles the rest. Elegant but uses stack space proportional to input length.
