Facebook Pixel

412. Fizz Buzz

EasyMathStringSimulation
Leetcode Link

Problem Description

This is the classic FizzBuzz problem. You're given an integer n, and you need to create and return a string array containing n elements (indexed from 1 to n).

For each position i from 1 to n, you need to determine what string to place in the array based on these rules:

  • If i is divisible by both 3 and 5, add "FizzBuzz"
  • If i is divisible by only 3, add "Fizz"
  • If i is divisible by only 5, add "Buzz"
  • If i is not divisible by 3 or 5, add the number itself as a string (e.g., "7")

The solution iterates through numbers from 1 to n. For each number, it first checks if it's divisible by 15 (which means divisible by both 3 and 5), then checks divisibility by 3 alone, then by 5 alone. If none of these conditions are met, the number is converted to a string and added to the result array.

For example, if n = 15, the output would be: ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz"]

The key insight in the solution is checking divisibility by 15 first (using i % 15 == 0), which is equivalent to checking if a number is divisible by both 3 and 5, since 15 is the least common multiple of 3 and 5.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The problem asks us to process each number from 1 to n and apply different transformations based on divisibility rules. The natural approach is to check each number sequentially and determine which rule applies.

The key observation is that numbers divisible by both 3 and 5 are actually divisible by 15 (since 15 is the least common multiple of 3 and 5). This means we can simplify our checks by testing for divisibility by 15 directly instead of checking both conditions separately.

The order of checking conditions matters. We must check for divisibility by 15 first because these numbers are also divisible by 3 and by 5. If we checked for divisibility by 3 or 5 first, we'd never reach the "FizzBuzz" case. For example, the number 15 is divisible by 3, so if we check i % 3 == 0 first, we'd incorrectly output "Fizz" instead of "FizzBuzz".

This leads to a straightforward if-else chain:

  1. First check if the number is divisible by both 3 and 5 (using i % 15 == 0)
  2. Then check if it's divisible by just 3
  3. Then check if it's divisible by just 5
  4. If none of the above, use the number itself

Since we need to process every number from 1 to n exactly once and apply a constant-time check for each, a simple loop with conditional statements gives us an efficient solution with O(n) time complexity.

Solution Approach

The solution uses a simple simulation approach, iterating through each integer from 1 to n and applying the divisibility rules to determine the appropriate string to add to our result array.

Implementation Steps:

  1. Initialize an empty list ans to store our results.

  2. Iterate through numbers from 1 to n (inclusive) using range(1, n + 1).

  3. Apply divisibility checks for each number i:

    • First check i % 15 == 0: If true, the number is divisible by both 3 and 5, so append "FizzBuzz".
    • Else check i % 3 == 0: If true, the number is divisible by 3, so append "Fizz".
    • Else check i % 5 == 0: If true, the number is divisible by 5, so append "Buzz".
    • Otherwise: Convert the number to a string using str(i) and append it.
  4. Return the result array after processing all numbers.

Why this order of checks? The condition for "FizzBuzz" (divisible by both 3 and 5) must be checked first. If we checked divisibility by 3 or 5 first, numbers like 15, 30, 45 would incorrectly produce "Fizz" or "Buzz" instead of "FizzBuzz" since they satisfy all three conditions.

Data Structure: We use a simple list/array to collect our results, which is the most natural choice since we need to return a collection of strings in order.

Time and Space Complexity:

  • Time: O(n) - We process each number from 1 to n exactly once.
  • Space: O(n) - We store n strings in our result array.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with n = 6 to see how each number is processed:

Step 1: Initialize empty result array: ans = []

Step 2: Process i = 1

  • Check 1 % 15 == 0? No (1 ÷ 15 = 0 remainder 1)
  • Check 1 % 3 == 0? No (1 ÷ 3 = 0 remainder 1)
  • Check 1 % 5 == 0? No (1 ÷ 5 = 0 remainder 1)
  • None match, so append "1"
  • Result: ["1"]

Step 3: Process i = 2

  • Check 2 % 15 == 0? No
  • Check 2 % 3 == 0? No
  • Check 2 % 5 == 0? No
  • None match, so append "2"
  • Result: ["1", "2"]

Step 4: Process i = 3

  • Check 3 % 15 == 0? No (3 ÷ 15 = 0 remainder 3)
  • Check 3 % 3 == 0? Yes (3 ÷ 3 = 1 remainder 0)
  • Append "Fizz"
  • Result: ["1", "2", "Fizz"]

Step 5: Process i = 4

  • Check 4 % 15 == 0? No
  • Check 4 % 3 == 0? No
  • Check 4 % 5 == 0? No
  • None match, so append "4"
  • Result: ["1", "2", "Fizz", "4"]

Step 6: Process i = 5

  • Check 5 % 15 == 0? No (5 ÷ 15 = 0 remainder 5)
  • Check 5 % 3 == 0? No (5 ÷ 3 = 1 remainder 2)
  • Check 5 % 5 == 0? Yes (5 ÷ 5 = 1 remainder 0)
  • Append "Buzz"
  • Result: ["1", "2", "Fizz", "4", "Buzz"]

Step 7: Process i = 6

  • Check 6 % 15 == 0? No
  • Check 6 % 3 == 0? Yes (6 ÷ 3 = 2 remainder 0)
  • Append "Fizz"
  • Result: ["1", "2", "Fizz", "4", "Buzz", "Fizz"]

Final Output: ["1", "2", "Fizz", "4", "Buzz", "Fizz"]

Notice how the order of checks matters. If we encounter i = 15, it would satisfy all three divisibility conditions (15 % 3 == 0, 15 % 5 == 0, and 15 % 15 == 0). By checking 15 first, we correctly output "FizzBuzz" instead of stopping at "Fizz" or "Buzz".

Solution Implementation

1class Solution:
2    def fizzBuzz(self, n: int) -> List[str]:
3        """
4        Generate FizzBuzz sequence from 1 to n.
5      
6        Args:
7            n: The upper limit of the sequence (inclusive)
8          
9        Returns:
10            A list of strings where:
11            - Numbers divisible by 3 are replaced with "Fizz"
12            - Numbers divisible by 5 are replaced with "Buzz"
13            - Numbers divisible by both 3 and 5 are replaced with "FizzBuzz"
14            - All other numbers are converted to strings
15        """
16        result = []
17      
18        # Iterate through numbers from 1 to n (inclusive)
19        for num in range(1, n + 1):
20            # Check divisibility by 15 first (divisible by both 3 and 5)
21            if num % 15 == 0:
22                result.append('FizzBuzz')
23            # Check divisibility by 3
24            elif num % 3 == 0:
25                result.append('Fizz')
26            # Check divisibility by 5
27            elif num % 5 == 0:
28                result.append('Buzz')
29            # For all other numbers, append the string representation
30            else:
31                result.append(str(num))
32              
33        return result
34
1class Solution {
2    /**
3     * Generates a FizzBuzz sequence from 1 to n.
4     * 
5     * @param n The upper limit of the sequence (inclusive)
6     * @return A list of strings representing the FizzBuzz sequence
7     * 
8     * Rules:
9     * - Numbers divisible by 3 are replaced with "Fizz"
10     * - Numbers divisible by 5 are replaced with "Buzz"
11     * - Numbers divisible by both 3 and 5 are replaced with "FizzBuzz"
12     * - All other numbers are converted to their string representation
13     */
14    public List<String> fizzBuzz(int n) {
15        // Initialize the result list to store the FizzBuzz sequence
16        List<String> result = new ArrayList<>();
17      
18        // Iterate through numbers from 1 to n (inclusive)
19        for (int currentNumber = 1; currentNumber <= n; currentNumber++) {
20            // Build the string representation for the current number
21            String currentString = "";
22          
23            // Check if divisible by 3
24            if (currentNumber % 3 == 0) {
25                currentString += "Fizz";
26            }
27          
28            // Check if divisible by 5
29            if (currentNumber % 5 == 0) {
30                currentString += "Buzz";
31            }
32          
33            // If not divisible by 3 or 5, use the number itself
34            if (currentString.length() == 0) {
35                currentString += currentNumber;
36            }
37          
38            // Add the processed string to the result list
39            result.add(currentString);
40        }
41      
42        return result;
43    }
44}
45
1class Solution {
2public:
3    vector<string> fizzBuzz(int n) {
4        // Initialize result vector to store output strings
5        vector<string> result;
6      
7        // Iterate through numbers from 1 to n inclusive
8        for (int i = 1; i <= n; ++i) {
9            // Initialize current string for this number
10            string currentString = "";
11          
12            // Check if number is divisible by 3
13            if (i % 3 == 0) {
14                currentString += "Fizz";
15            }
16          
17            // Check if number is divisible by 5
18            if (i % 5 == 0) {
19                currentString += "Buzz";
20            }
21          
22            // If not divisible by 3 or 5, use the number itself
23            if (currentString.empty()) {
24                currentString = to_string(i);
25            }
26          
27            // Add the resulting string to the result vector
28            result.push_back(currentString);
29        }
30      
31        // Return the complete result vector
32        return result;
33    }
34};
35
1/**
2 * Generates an array of strings following the FizzBuzz pattern
3 * @param n - The upper limit of the range (inclusive)
4 * @returns An array of strings where multiples of 3 are "Fizz", 
5 *          multiples of 5 are "Buzz", multiples of both are "FizzBuzz",
6 *          and other numbers are their string representation
7 */
8function fizzBuzz(n: number): string[] {
9    // Initialize the result array to store FizzBuzz sequence
10    const result: string[] = [];
11  
12    // Iterate through numbers from 1 to n (inclusive)
13    for (let i = 1; i <= n; i++) {
14        // Check if divisible by both 3 and 5 (i.e., divisible by 15)
15        if (i % 15 === 0) {
16            result.push('FizzBuzz');
17        } 
18        // Check if divisible by 3 only
19        else if (i % 3 === 0) {
20            result.push('Fizz');
21        } 
22        // Check if divisible by 5 only
23        else if (i % 5 === 0) {
24            result.push('Buzz');
25        } 
26        // For all other numbers, add the number as a string
27        else {
28            result.push(i.toString());
29        }
30    }
31  
32    return result;
33}
34

Time and Space Complexity

Time Complexity: O(n)

The algorithm iterates through numbers from 1 to n exactly once using a single for loop. In each iteration, it performs constant-time operations: modulo calculations (%), comparisons, and appending to a list. Since we perform a constant amount of work for each of the n numbers, the overall time complexity is O(n), where n is the input integer.

Space Complexity: O(1) (excluding the output array)

If we exclude the space required for the output array ans (which is necessary to store the result), the algorithm uses only a constant amount of extra space. The only additional variable used is the loop iterator i, which requires O(1) space regardless of the input size. No additional data structures or recursive calls are made that would scale with the input.

If we include the output array in our space complexity analysis, it would be O(n) since we store exactly n strings in the result list.

Common Pitfalls

1. Incorrect Order of Conditional Checks

The Pitfall: A frequent mistake is checking for divisibility by 3 or 5 before checking for divisibility by both. This leads to incorrect output for numbers that are multiples of 15.

Incorrect Implementation:

# WRONG - This will never print "FizzBuzz"
for num in range(1, n + 1):
    if num % 3 == 0:
        result.append('Fizz')  # 15 would stop here
    elif num % 5 == 0:
        result.append('Buzz')
    elif num % 15 == 0:  # This condition is unreachable for multiples of 15
        result.append('FizzBuzz')
    else:
        result.append(str(num))

Why It Fails: When num = 15, the first condition num % 3 == 0 evaluates to true, so the code appends "Fizz" and never reaches the check for divisibility by 15.

The Solution: Always check the most restrictive condition first. Since numbers divisible by both 3 and 5 are a subset of numbers divisible by 3 (and also a subset of numbers divisible by 5), check for divisibility by 15 before checking for divisibility by 3 or 5 individually.

Correct Implementation:

for num in range(1, n + 1):
    if num % 15 == 0:  # Most restrictive condition first
        result.append('FizzBuzz')
    elif num % 3 == 0:
        result.append('Fizz')
    elif num % 5 == 0:
        result.append('Buzz')
    else:
        result.append(str(num))

2. Using Separate Conditions Instead of elif

The Pitfall: Using multiple independent if statements instead of elif can lead to multiple strings being appended for a single number.

Incorrect Implementation:

# WRONG - Multiple conditions can be true for the same number
for num in range(1, n + 1):
    if num % 3 == 0:
        result.append('Fizz')
    if num % 5 == 0:  # This is a separate check, not elif
        result.append('Buzz')
    if num % 15 == 0:
        result.append('FizzBuzz')
    if num % 3 != 0 and num % 5 != 0:
        result.append(str(num))

Why It Fails: For num = 15, all three conditions involving modulo operations would be true, resulting in three strings ("Fizz", "Buzz", and "FizzBuzz") being appended instead of just "FizzBuzz".

The Solution: Use elif statements to ensure only one condition is executed per number, creating mutually exclusive branches.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More