Fizz Buzz — A Clean, Extensible Solution
Solve Fizz Buzz with clean code and explore the string-concatenation pattern that avoids nested if-else. Python, Java, C++, and complexity analysis included.
What you'll learn
- ✓Solve the problem with the classic if-else approach
- ✓Refactor to the string-concatenation pattern
- ✓Understand why interviewers still ask Fizz Buzz
- ✓Implement the same logic in Python, Java, and C++
- ✓Reason about extensibility for FizzBuzzBazz variants
Prerequisites
- •Loops and conditionals in any language
Fizz Buzz is the most famous interview screen of all time. Today we focus on writing it well, not just writing it. The real skill is producing code that is easy to extend if the interviewer adds a third divisor.
The Problem
Given an integer n, return a string array answer of length n where:
answer[i] = "FizzBuzz"ifi + 1is divisible by 3 and 5.answer[i] = "Fizz"ifi + 1is divisible by 3.answer[i] = "Buzz"ifi + 1is divisible by 5.answer[i] = str(i + 1)otherwise.
The 1-indexed convention is what trips people up first. Loop from 1 to n inclusive, not 0 to n - 1.
Intuition
The straightforward version uses nested if-else and reads almost identically to the spec: check divisibility by 15 first, then 3, then 5, otherwise return the number as a string. This is perfectly acceptable — but it does not extend well. If the interviewer adds a fourth divisor, you would need eight branches.
The string-concatenation pattern scales linearly with the number of words. Build an empty string, append “Fizz” if divisible by 3, append “Buzz” if divisible by 5. If both apply you get “FizzBuzz” for free. If neither, fall back to the number. Adding a third rule is one new if block.
Explanation with Example
Take n = 15. We loop i from 1 to 15 inclusive. At i = 3, the divisible-by-3 check appends “Fizz” so s = "Fizz". At i = 5, “Buzz” is appended. At i = 15, both checks fire and s = "FizzBuzz". For numbers like 1, 2, 4 where neither divides, s stays empty and we fall back to the string of the number.
i | i%3 | i%5 | s | output
------+-----+-----+--------------+----------
1 | 1 | 1 | "" | "1"
2 | 2 | 2 | "" | "2"
3 | 0 | 3 | "Fizz" | "Fizz"
4 | 1 | 4 | "" | "4"
5 | 2 | 0 | "Buzz" | "Buzz"
6 | 0 | 1 | "Fizz" | "Fizz"
... | | | |
15 | 0 | 0 | "FizzBuzz" | "FizzBuzz" <-- both rules fire Code
class Solution:
def fizzBuzz(self, n: int) -> list[str]:
out = []
for i in range(1, n + 1):
s = ""
if i % 3 == 0:
s += "Fizz"
if i % 5 == 0:
s += "Buzz"
out.append(s if s else str(i))
return outclass Solution {
public List<String> fizzBuzz(int n) {
List<String> out = new ArrayList<>(n);
for (int i = 1; i <= n; i++) {
StringBuilder sb = new StringBuilder();
if (i % 3 == 0) sb.append("Fizz");
if (i % 5 == 0) sb.append("Buzz");
out.add(sb.length() == 0 ? Integer.toString(i) : sb.toString());
}
return out;
}
}class Solution {
public:
vector<string> fizzBuzz(int n) {
vector<string> out;
out.reserve(n);
for (int i = 1; i <= n; i++) {
string s;
if (i % 3 == 0) s += "Fizz";
if (i % 5 == 0) s += "Buzz";
out.push_back(s.empty() ? to_string(i) : s);
}
return out;
}
};Edge Cases
n = 0: return an empty list. The loop never runs.n = 1: return["1"].- Very large
n: no special handling needed; allocation is linear. - Off-by-one: remember the 1-indexed output.
Complexity Analysis
Time complexity is O(n). We iterate once from 1 to n, doing constant-time work per element.
Space complexity is O(n) for the output list. The problem requires returning all n answers, so this is unavoidable. Auxiliary space (the temporary s or StringBuilder) is O(1).
How to Explain It in an Interview
Fizz Buzz is not testing whether you know modulo. It is testing whether you can read the spec carefully (notice the 1-indexing), handle the “both” case correctly without copy-pasting strings, convert integers to strings in your language of choice, and refactor when the requirements change. Hit those four notes and you pass.
Related Problems
- Multiples of 3 and 5
- Count and Say
- String formatting variants in any starter set
Wrap up
Fizz Buzz endures because it cleanly separates the people who can write a correct, extensible loop from the people who cannot. Memorize the string-concatenation pattern — it is shorter, it scales, and it signals that you have seen the problem before in a thoughtful way.
Related articles
- DSA Find Median from Data Stream — Two-Heaps Pattern
Solve Find Median from Data Stream with two heaps. Learn the balance invariant, why it gives O(log n) inserts and O(1) median, and the common pitfalls.
- DSA Find Minimum in Rotated Sorted Array — Binary Search
Solve Find Minimum in Rotated Sorted Array in logarithmic time with a modified binary search. Learn the pivot-finding invariant, edge cases, and how to extend the pattern to related problems.
- DSA Jump Game — Greedy Max-Reach Beats DP
Solve Jump Game with a single-pass greedy max-reach approach. Compare it against the dynamic programming solution and learn when greedy is provably optimal.
- DSA Maximum Product Subarray — Tracking Min and Max
Solve Maximum Product Subarray in linear time by tracking running min and max. Learn why negative numbers flip the role of min and max and see clean code.