title: Add Digits
slug: add-digits
difficulty: easy
leetcode_id: 258
leetcode_url: https://leetcode.com/problems/add-digits/
categories:
  - math
patterns:
  - greedy

function_signature: "def add_digits(num: int) -> int:"

test_cases:
  visible:
    - input: { num: 38 }
      expected: 2
    - input: { num: 0 }
      expected: 0
    - input: { num: 9 }
      expected: 9
  hidden:
    - input: { num: 18 }
      expected: 9
    - input: { num: 123 }
      expected: 6
    - input: { num: 199 }
      expected: 1

description: |
  Given an integer `num`, repeatedly add all its digits until the result has only one digit, and return it.

  This process of summing digits repeatedly is known as finding the **digital root** of a number.

constraints: |
  - `0 <= num <= 2^31 - 1`

examples:
  - input: "num = 38"
    output: "2"
    explanation: "The process is: 38 → 3 + 8 → 11 → 1 + 1 → 2. Since 2 has only one digit, return it."
  - input: "num = 0"
    output: "0"
    explanation: "0 is already a single digit, so return it."

explanation:
  intuition: |
    Imagine you have a number like `38`. You sum its digits: `3 + 8 = 11`. Still two digits, so you repeat: `1 + 1 = 2`. Now you have a single digit — that's your answer.

    While a loop-based simulation works, there's a **beautiful mathematical shortcut** hiding in plain sight.

    Think about what happens when you sum a number's digits. Take `38`:
    - `38 = 3 × 10 + 8 = 3 × (9 + 1) + 8 = 3 × 9 + 3 + 8`

    Notice that `3 × 9` is divisible by 9, so it contributes nothing to the remainder when dividing by 9. The digit sum `3 + 8 = 11` has the **same remainder when divided by 9** as the original number `38`.

    This pattern holds recursively! The final single digit (called the **digital root**) is simply the number's remainder when divided by 9 — with one small adjustment for multiples of 9.

  approach: |
    We can solve this two ways: the straightforward simulation and the elegant O(1) math formula.

    **Simulation Approach**

    **Step 1: Handle the base case**

    - If `num` is already a single digit (`num < 10`), return it directly

    &nbsp;

    **Step 2: Sum the digits**

    - Extract each digit using `num % 10` and add to a running sum
    - Remove the last digit with `num // 10`
    - Repeat until `num` becomes 0

    &nbsp;

    **Step 3: Recurse or loop**

    - If the sum is still multi-digit, repeat the process
    - Continue until you reach a single digit

    &nbsp;

    **O(1) Mathematical Approach (Digital Root Formula)**

    **Step 1: Handle zero**

    - If `num == 0`, return `0` (special case)

    &nbsp;

    **Step 2: Apply the digital root formula**

    - For any positive number, the digital root is `1 + (num - 1) % 9`
    - This formula works because:
      - If `num % 9 == 0` and `num > 0`, the digital root is `9`
      - Otherwise, the digital root is `num % 9`
    - The formula `1 + (num - 1) % 9` elegantly handles both cases

    &nbsp;

    The mathematical approach avoids all loops and runs in constant time regardless of the input size.

  common_pitfalls:
    - title: Forgetting the Zero Case
      description: |
        When using the mathematical formula, `0` requires special handling.

        The formula `1 + (num - 1) % 9` would give:
        - `1 + (-1) % 9 = 1 + (-1) = 0` (in Python, which handles negative modulo correctly)

        However, in some languages, `-1 % 9` might return `-1` instead of `8`, leading to wrong results. Always check for `num == 0` explicitly for portability.
      wrong_approach: "Applying the formula without checking for zero"
      correct_approach: "Handle num == 0 as a special case first"

    - title: Misunderstanding the Formula
      description: |
        A common mistake is using `num % 9` directly. This fails for multiples of 9:
        - `9 % 9 = 0`, but the digital root of 9 is `9`, not `0`
        - `18 % 9 = 0`, but `1 + 8 = 9`, not `0`

        The formula `1 + (num - 1) % 9` shifts the range from `[0, 8]` to `[1, 9]`, correctly mapping multiples of 9 to `9` instead of `0`.
      wrong_approach: "num % 9 (returns 0 for multiples of 9)"
      correct_approach: "1 + (num - 1) % 9 (returns 9 for multiples of 9)"

    - title: Inefficient Simulation for Large Numbers
      description: |
        The simulation approach is O(log n) per iteration, and the number of iterations is O(log log n) in practice. While this is acceptable, it's far less elegant than the O(1) mathematical solution.

        For very large numbers (up to 2^31 - 1), the simulation still works but is unnecessarily complex when a direct formula exists.
      wrong_approach: "Always using simulation without knowing the math"
      correct_approach: "Use the O(1) formula for optimal efficiency"

  key_takeaways:
    - "**Digital root concept**: The digital root of a number is the single digit obtained by repeatedly summing digits — it equals `1 + (n-1) % 9` for positive n"
    - "**Modular arithmetic insight**: Digit sums preserve remainders mod 9 because 10 ≡ 1 (mod 9), so each place value contributes its digit directly"
    - "**O(1) vs simulation tradeoff**: While simulation is intuitive, recognising mathematical patterns can reduce complexity dramatically"
    - "**Foundation for number theory problems**: This technique appears in problems involving divisibility rules, casting out nines, and checksum algorithms"

  time_complexity: "O(1). The mathematical formula computes the result directly with a single modulo operation."
  space_complexity: "O(1). Only a constant amount of space is used regardless of input size."

solutions:
  - approach_name: Digital Root Formula
    is_optimal: true
    code: |
      def add_digits(num: int) -> int:
          # Special case: 0 is already a single digit
          if num == 0:
              return 0

          # Digital root formula: maps to range [1, 9]
          # Works because digit sums preserve remainder mod 9
          return 1 + (num - 1) % 9
    explanation: |
      **Time Complexity:** O(1) — Single arithmetic operation.

      **Space Complexity:** O(1) — No additional space used.

      The digital root formula leverages the mathematical property that summing digits preserves the remainder when dividing by 9. By using `1 + (num - 1) % 9`, we correctly handle multiples of 9 (which should return 9, not 0).

  - approach_name: Iterative Simulation
    is_optimal: false
    code: |
      def add_digits(num: int) -> int:
          # Keep summing digits until we have a single digit
          while num >= 10:
              digit_sum = 0
              # Extract and sum each digit
              while num > 0:
                  digit_sum += num % 10  # Get last digit
                  num //= 10             # Remove last digit
              num = digit_sum

          return num
    explanation: |
      **Time Complexity:** O(log n) — We process each digit, and digit count is proportional to log(n).

      **Space Complexity:** O(1) — Only tracking the running sum.

      This approach simulates the exact process described in the problem: repeatedly sum digits until reaching a single digit. While correct and intuitive, it's less efficient than the mathematical formula.

  - approach_name: Recursive Simulation
    is_optimal: false
    code: |
      def add_digits(num: int) -> int:
          # Base case: single digit
          if num < 10:
              return num

          # Recursive case: sum digits and recurse
          digit_sum = 0
          while num > 0:
              digit_sum += num % 10
              num //= 10

          return add_digits(digit_sum)
    explanation: |
      **Time Complexity:** O(log n) — Same as iterative, processing each digit.

      **Space Complexity:** O(log log n) — Recursion depth is the number of iterations needed.

      A recursive approach that mirrors the problem description exactly. Each call sums the digits and recurses until reaching a single digit. The recursion depth is very shallow (typically 2-3 calls for any 32-bit integer).
