Skip to content
← All posts

Fizz Buzz: The Classic Interview Warm-Up

4 min read
leetcodeproblemeasymathstrings

LeetCode 412. Fizz Buzz asks you to return a list of strings for the numbers 1 through n. For each number, if it is divisible by 3 you output "Fizz", if divisible by 5 you output "Buzz", if divisible by both you output "FizzBuzz", and otherwise you output the number itself as a string.

It is one of the most well-known programming exercises in existence, and for good reason.

12Fizz4BuzzFizz78FizzBuzz11Fizz1314FizzBuzzNumberFizz (x3)Buzz (x5)FizzBuzz (x15)
Numbers 1 through 15. Multiples of 3 become "Fizz," multiples of 5 become "Buzz," and multiples of both become "FizzBuzz."

Why this problem matters

Fizz Buzz is often dismissed as trivial. Candidates breeze past it and interviewers use it as a warm-up before "real" questions. But the problem quietly tests something important: whether you structure conditional logic cleanly.

The most common mistake is checking divisibility by 3 and 5 separately before checking divisibility by 15. If you write if i % 3 == 0 first and elif i % 5 == 0 second, then numbers like 15 will match the first condition and output "Fizz" instead of "FizzBuzz". The fix is simple, but the fact that so many people get it wrong on a first pass shows how easy it is to mis-order conditions.

This is the same class of bug that causes issues in production code: overlapping conditions where the more specific case needs to come first. Fizz Buzz trains you to think about condition ordering, a habit that pays off in every if-elif chain you ever write.

The clean solution

The most direct approach checks divisibility by 15 first (the most specific case), then 3, then 5, and falls through to the default.

def fizz_buzz(n: int) -> list[str]:
    result = []
    for i in range(1, n + 1):
        if i % 15 == 0:
            result.append("FizzBuzz")
        elif i % 3 == 0:
            result.append("Fizz")
        elif i % 5 == 0:
            result.append("Buzz")
        else:
            result.append(str(i))
    return result

Step-by-step walkthrough

Step 1: Check i = 15 (divisible by both 3 and 5)

i =15
i % 15 == 0True
i % 3 == 0skipped
i % 5 == 0skipped
output:"FizzBuzz"

Check divisibility by 15 first. Since 15 is divisible by both 3 and 5, we output "FizzBuzz" and skip the remaining checks.

Step 2: Check i = 9 (divisible by 3 only)

i =9
i % 15 == 0False
i % 3 == 0True
i % 5 == 0skipped
output:"Fizz"

9 is not divisible by 15, so we move on. It is divisible by 3, so we output "Fizz."

Step 3: Check i = 10 (divisible by 5 only)

i =10
i % 15 == 0False
i % 3 == 0False
i % 5 == 0True
output:"Buzz"

10 is not divisible by 15 or 3, but it is divisible by 5. We output "Buzz."

Step 4: Check i = 7 (not divisible by 3 or 5)

i =7
i % 15 == 0False
i % 3 == 0False
i % 5 == 0False
output:"7"

7 fails all three divisibility checks. The default case converts the number to its string representation.

The key takeaway: always check the most specific condition first. Checking i % 15 == 0 before checking i % 3 == 0 or i % 5 == 0 prevents the more general cases from swallowing the combined case.

A string-building alternative

Instead of checking divisibility by 15 as a separate case, you can build the output string by concatenating "Fizz" and "Buzz" independently. This approach scales better if you need to add more divisors later (say, "Jazz" for multiples of 7).

def fizz_buzz(n: int) -> list[str]:
    result = []
    for i in range(1, n + 1):
        s = ""
        if i % 3 == 0:
            s += "Fizz"
        if i % 5 == 0:
            s += "Buzz"
        result.append(s or str(i))
    return result

Notice that these are plain if statements, not elif. Each condition is independent, so "Fizz" and "Buzz" naturally combine into "FizzBuzz" when both apply. The s or str(i) at the end handles the default case: if s is still empty, Python treats it as falsy and falls back to the number string.

This version avoids the condition-ordering trap entirely. You do not need to check for 15 because the concatenation handles it automatically.

Complexity analysis

ApproachTimeSpace
If-elif chainO(n)O(n) for the output list
String concatenationO(n)O(n) for the output list

Both approaches iterate once through the range 1 to n and perform constant work per iteration. The output list itself is O(n), and no extra data structures are needed beyond it.

Edge cases to watch for

  • n = 1: The output is just ["1"]. No multiples of 3 or 5 exist yet.
  • n = 3: The output is ["1", "2", "Fizz"]. The first Fizz appears.
  • n = 15: The first FizzBuzz appears at position 15. This is the smallest number divisible by both 3 and 5.
  • Large n: No special handling needed. The algorithm is linear and works for any positive integer.

The building blocks

Fizz Buzz decomposes into two reusable ideas that appear across many problems.

1. Modular arithmetic for divisibility

Using n % k == 0 to test whether n is divisible by k is the same operation you use in Palindrome Number to extract digits, in Reverse Integer to peel off the last digit, and in Happy Number to compute digit-square sums. The modulo operator is one of the most versatile tools in your kit.

2. Condition ordering (most specific first)

When multiple conditions overlap, always check the most restrictive case first. This principle appears in every problem with branching logic. In Fizz Buzz, the overlap is between multiples of 3, multiples of 5, and multiples of 15. In other problems, it might be edge cases that need to be caught before the general case runs.

From understanding to recall

Fizz Buzz is easy to understand. But when an interviewer asks a variant, like adding a third divisor or handling negative numbers, you want condition ordering and string-building to be automatic, not something you rediscover under pressure.

That is what spaced repetition solves. CodeBricks drills each building block at increasing intervals. After a few review cycles, the modular arithmetic pattern and the condition-ordering habit become second nature. You stop thinking about them and start applying them.

Related posts

  • Palindrome Number - Digit extraction with modular arithmetic, the same mod operator used here to test divisibility
  • Add Digits - Repeatedly applying modular arithmetic to reduce a number, another simple math problem with a deeper pattern
  • Count and Say - String building from a sequence of rules, similar to how the concatenation approach constructs output