Problem overview
The function fizzBuzz(n: Int) prints the numbers from 1 to n, one per line, with three replacements. A multiple of 3 prints Fizz instead of the number, a multiple of 5 prints Buzz, and a number that is a multiple of both prints FizzBuzz.
For n = 15 the output is 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz. The statement asks for O(n) time and O(1) space.
Approach
Visit each number once with a for loop and decide what to print for it. The decision uses the remainder operator %: i % 3 == 0 means i is a multiple of 3.
The order of the checks matters. A number such as 15 is a multiple of 3 and of 5, and it must print FizzBuzz, not Fizz. So the case for both has to be tested first. A number is a multiple of both 3 and 5 exactly when it is a multiple of 15, so one check, i % 15 == 0, covers it.
Step by step
- Loop from 1 to
n. The range1..nincludes both ends, so the loop reachesnitself. - Test multiples of 15 first. If
i % 15 == 0, printFizzBuzz. - Then test multiples of 3. In the
else if, a number that gets here is not a multiple of 15, so printingFizzis right. - Then test multiples of 5 the same way and print
Buzz. - Otherwise print the number itself in the final
else.
Exactly one branch runs per number, because each else is reached only when the earlier conditions were false.
Tracing three numbers
Follow the checks for 9, 10 and 15 to see how the chain behaves:
9:9 % 15is 9, not 0, so the first test fails.9 % 3is 0, so the loop printsFizzand skips the remaining tests.10:10 % 15is 10 and10 % 3is 1, so both tests fail.10 % 5is 0, so it printsBuzz.15:15 % 15is 0, so it printsFizzBuzzimmediately, and the checks for 3 and 5 are never reached.
Another way to write the test
Instead of testing 15, the first condition could test both factors: i % 3 == 0 && i % 5 == 0. It gives the same answers, because a number divisible by 3 and by 5 is divisible by 15 (3 and 5 share no factor). The i % 15 form is shorter and does one remainder operation instead of two. Both are O(1) per number. A different idea is to build the word in pieces, adding Fizz when divisible by 3 and Buzz when divisible by 5, and printing the number only if the word is still empty. The lesson uses the plain chain because it is the easiest to read.
The solution in Kotlin
class Solution {
fun fizzBuzz(n: Int) {
for (i in 1..n) {
if (i % 15 == 0) {
println("FizzBuzz")
} else if (i % 3 == 0) {
println("Fizz")
} else if (i % 5 == 0) {
println("Buzz")
} else {
println(i)
}
}
}
}
Kotlin details:
- Ranges.
1..nis inclusive on both ends. If you wrote1 until n, the loop would stop atn - 1and miss the last line. Forn = 0the range is empty and the loop doesn't run. - The loop variable.
iis declared by theforitself and is read-only inside the body, so you can't assign to it. printlntakes any value. It prints a string or anIntthe same way, so the last branch needs no conversion.- Remainder.
%on non-negativeIntvalues gives what is left after division, so9 % 3is0and10 % 3is1.
The same logic with when
Kotlin's when without an argument checks its conditions from top to bottom and runs the first branch that is true, which is the same behaviour as the else if chain:
when {
i % 15 == 0 -> println("FizzBuzz")
i % 3 == 0 -> println("Fizz")
i % 5 == 0 -> println("Buzz")
else -> println(i)
}
Placed inside the loop, it prints the same 15 lines for n = 15. The order rule is unchanged: the 15 branch must come first.
Complexity
Time: O(n). The loop makes exactly n passes and does a constant amount of work in each.
Space: O(1). The only variable is the loop counter. Nothing is stored, because each line is printed as soon as it is decided.
Edge cases and common mistakes
- Checking 3 before 15. Then 15 prints
Fizz, because the first matching branch wins and theFizzBuzzbranch is never reached. The both-case must come first. - Using separate
ifstatements. With independentifstatements, 15 would printFizzandBuzzon two separate lines, and the plain-number case would need its own guard to stay silent. Theelse ifchain makes the cases exclusive. - Printing the wrong text. The words are case-sensitive:
fizzbuzzorFizzbuzzwon't matchFizzBuzz. - Starting at 0. Zero is a multiple of every number, so a loop from 0 would print
FizzBuzzfirst. - Stopping short.
1 until nori < nmissesn. - Using
=instead of==. A comparison needs==. In Kotlin, assignment is not an expression, soif (i % 3 = 0)doesn't compile.
Testing
n = 1prints1.n = 3prints1,2,Fizz.n = 5prints1,2,Fizz,4,Buzz.n = 15ends withFizzBuzz.n = 30ends withBuzzon line 25,Fizzon line 27 andFizzBuzzon line 30.