Problem overview
The method fizzBuzz(_ n: Int) prints the numbers from 1 to n, one per line, with three replacements: multiples of 3 print Fizz, multiples of 5 print Buzz, and numbers that are multiples of both print FizzBuzz. It returns nothing.
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 and decide what to print for it. The decision uses the remainder operator %: i % 3 == 0 means i is a multiple of 3. The one subtle point is the order of the checks. A number divisible by both 3 and 5 also passes the "multiple of 3" test, so the combined case must be tested first. A number is a multiple of both exactly when it is a multiple of 15, because 3 and 5 share no factor.
Step by step
- Loop from 1 to
nwith the closed range1...n, which includesn. - Check 15 first. If
i % 15 == 0, printFizzBuzz. - Then check 3, in an
else ifbranch, and printFizz. - Then check 5 and print
Buzz. - Otherwise print the number itself in the final
else.
The solution in Swift
class Solution {
func fizzBuzz(_ n: Int) {
for i in 1...n {
if i % 15 == 0 {
print("FizzBuzz")
} else if i % 3 == 0 {
print("Fizz")
} else if i % 5 == 0 {
print("Buzz")
} else {
print(i)
}
}
}
}
Swift details:
1...nis a closed range, and the loop constantiis a freshInton each pass. Swift also allows1...nonly whennis at least 1; a range like1...0stops the program at run time. The statement promises a positiven, so this is safe here.printaccepts both strings and integers, and adds the newline.- The conditions have no parentheses, but the braces around each branch are mandatory.
- A
switchon the pair of remainders, such asswitch (i % 3, i % 5), is a common alternative, but theifchain is the simplest.
Why check 15 and not "3 and 5"
Writing i % 3 == 0 && i % 5 == 0 is equally correct and arguably reads more directly. The % 15 form works because a number divisible by two coprime numbers is divisible by their product. Whichever you pick, the rule is the same: the most specific condition goes first, and each later branch only runs for numbers that failed the earlier ones. That is what lets the else if branches stay short: when the % 3 test runs, you already know the number is not a multiple of 15.
The remainder operator
The operator % gives the remainder after integer division, so 15 % 3 is 0 and 7 % 3 is 1. A remainder of 0 means the first number divides evenly. In Swift, % works on integers and takes the sign of the left operand, so -3 % 5 is -3. That doesn't matter here, since i is never negative, but it is worth remembering when you test divisibility on numbers that can be.
Tracing a few values
i | i % 15 | i % 3 | i % 5 | Printed |
|---|---|---|---|---|
| 7 | 7 | 1 | 2 | 7 |
| 9 | 9 | 0 | 4 | Fizz |
| 10 | 10 | 1 | 0 | Buzz |
| 15 | 0 | 0 | 0 | FizzBuzz |
Other ways to write it
Instead of testing 15 explicitly, you can build the word from pieces and fall back to the number when the word stays empty:
func fizzBuzz2(_ n: Int) {
for i in 1...n {
var word = ""
if i % 3 == 0 { word += "Fizz" }
if i % 5 == 0 { word += "Buzz" }
print(word.isEmpty ? String(i) : word)
}
}
This prints the same 15 lines for n = 15. Swift's switch must be exhaustive and can match a tuple of remainders, which makes the four cases explicit:
func fizzBuzz3(_ n: Int) {
for i in 1...n {
switch (i % 3, i % 5) {
case (0, 0): print("FizzBuzz")
case (0, _): print("Fizz")
case (_, 0): print("Buzz")
default: print(i)
}
}
}
This also prints the same output. Both variants are still O(n) time. The lesson's version is the shortest to read, and the other two are worth knowing because they scale better when more rules (say, a "Bazz" for 7) are added: the string-building form needs one new line, while the if chain needs a new combination for every pair of rules.
Complexity
Time: O(n). The loop runs exactly n times and each pass does a constant amount of work. Space: O(1). Only the loop constant is used, and the output is printed, not stored.
Edge cases and common mistakes
- Testing 3 before 15. Then 15 prints
Fizzand theFizzBuzzbranch can never run. - Using two independent
ifstatements without anelse, which prints both a word and the number for some inputs. - Using
1..<n. The half-open range stops beforen, so the last line is missing. n = 1. Prints just1.- Starting at 0. A loop
for i in 0..<nlooks natural but is wrong twice. It prints a line for 0, and since0 % 15 == 0that line isFizzBuzz; it also stops beforen. The range1...nfixes both. - Printing extra text. The output must be the exact words or numbers, one per line. Adding labels, commas or spaces around them makes the lines differ from the expected ones.
- Returning instead of printing. The method has no return value. Collecting the lines in an array and returning it would leave the console empty.
- Spelling. The words are case-sensitive:
Fizz,Buzz,FizzBuzz.
Testing
fizzBuzz(15)prints the sequence from the example, ending inFizzBuzz.fizzBuzz(5)prints 1, 2, Fizz, 4, Buzz.fizzBuzz(1)prints 1.
