Facebook Pixel

1006. Clumsy Factorial

MediumStackMathSimulation
Leetcode Link

Problem Description

The clumsy factorial is a variation of the regular factorial where we apply operations in a specific rotating pattern.

In a regular factorial, we multiply all positive integers from 1 to n. For example, factorial(10) = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1.

In a clumsy factorial, we start with n and work our way down to 1, but instead of only using multiplication, we rotate through four operations in this exact order:

  1. Multiply *
  2. Divide / (using floor division)
  3. Add +
  4. Subtract -

For example, clumsy(10) = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1.

The operations follow standard arithmetic rules:

  • Multiplication and division are performed before addition and subtraction
  • Multiplication and division are evaluated from left to right
  • Division uses floor division (rounds down to the nearest integer)

Given an integer n, you need to calculate and return the clumsy factorial of n.

The key insight is that the expression needs to be evaluated respecting operator precedence. In the example above:

  • First calculate: 10 * 9 = 90, then 90 / 8 = 11
  • Then calculate: 6 * 5 = 30, then 30 / 4 = 7
  • Then calculate: 2 * 1 = 2
  • Finally combine: 11 + 7 - 7 + 3 - 2 = 12
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we look at the clumsy factorial expression like 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1, we need to respect operator precedence. This means multiplication and division must be evaluated first before addition and subtraction.

The key observation is that we can group the operations into chunks based on operator precedence:

  • 10 * 9 / 8 forms one chunk (evaluates to 11)
  • + 7 is added
  • - 6 * 5 / 4 forms another chunk (evaluates to -7)
  • + 3 is added
  • - 2 * 1 forms the last chunk (evaluates to -2)

This naturally suggests using a stack to handle the calculation. Why? Because:

  1. When we encounter multiplication or division, we need to immediately evaluate it with the previous number (since these have higher precedence)
  2. When we encounter addition or subtraction, we can defer the calculation until the end

The pattern becomes clear:

  • For the first number n, push it onto the stack
  • When we see *, pop the top, multiply with the current number, and push back
  • When we see /, pop the top, divide by the current number, and push back
  • When we see +, just push the current number (it will be added later)
  • When we see -, push the negative of the current number (so we can sum everything at the end)

By tracking which operation we're on using a counter k that cycles through 0, 1, 2, 3 (representing *, /, +, -), we can simulate the entire process. At the end, the sum of all elements in the stack gives us our answer because we've already handled the multiplication/division immediately, and the remaining stack contains terms to be added or subtracted.

Learn more about Stack and Math patterns.

Solution Approach

We implement the solution using a stack and simulation approach.

Initialization:

  • Create a stack stk and push the initial value n into it
  • Initialize a counter k = 0 to track which operation we're currently on in the rotation

Main Process: We iterate through numbers from n-1 down to 1, and for each number x, we perform different operations based on the current value of k:

  1. When k = 0 (Multiplication):

    • Pop the top element from the stack
    • Multiply it by x
    • Push the result back onto the stack
    • This handles expressions like 10 * 9
  2. When k = 1 (Division):

    • Pop the top element from the stack
    • Divide it by x using floor division (int(stk.pop() / x))
    • Push the result back onto the stack
    • This completes chunks like 90 / 8 = 11
  3. When k = 2 (Addition):

    • Simply push x onto the stack
    • We don't need to perform the addition immediately since it has lower precedence
  4. When k = 3 (Subtraction):

    • Push -x onto the stack (negative value)
    • By storing the negative, we can sum all values at the end

Update Counter: After each operation, update k = (k + 1) % 4 to rotate through the four operations cyclically.

Final Result: Return sum(stk) - the sum of all elements in the stack. This works because:

  • Multiplication and division operations have already been evaluated and their results are in the stack
  • Addition terms are stored as positive values
  • Subtraction terms are stored as negative values
  • Summing them all gives the correct final result

Example Walkthrough for n = 10:

  • Start: stk = [10], k = 0
  • x = 9: multiply → stk = [90], k = 1
  • x = 8: divide → stk = [11], k = 2
  • x = 7: add → stk = [11, 7], k = 3
  • x = 6: subtract → stk = [11, 7, -6], k = 0
  • x = 5: multiply → stk = [11, 7, -30], k = 1
  • x = 4: divide → stk = [11, 7, -7], k = 2
  • x = 3: add → stk = [11, 7, -7, 3], k = 3
  • x = 2: subtract → stk = [11, 7, -7, 3, -2], k = 0
  • x = 1: multiply → stk = [11, 7, -7, 3, -2], k = 1
  • Final: sum([11, 7, -7, 3, -2]) = 12

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 calculating clumsy(5) = 5 * 4 / 3 + 2 - 1.

Initial Setup:

  • Stack: [5]
  • Counter k = 0 (tracks which operation: 0=multiply, 1=divide, 2=add, 3=subtract)
  • We'll process numbers from 4 down to 1

Step-by-Step Execution:

Step 1: Process number 4 (k=0, multiply)

  • Current stack: [5]
  • Operation: Pop 5, compute 5 * 4 = 20, push back
  • New stack: [20]
  • Update: k = 1

Step 2: Process number 3 (k=1, divide)

  • Current stack: [20]
  • Operation: Pop 20, compute 20 / 3 = 6 (floor division), push back
  • New stack: [6]
  • Update: k = 2

Step 3: Process number 2 (k=2, add)

  • Current stack: [6]
  • Operation: Push 2 directly (we'll add it later)
  • New stack: [6, 2]
  • Update: k = 3

Step 4: Process number 1 (k=3, subtract)

  • Current stack: [6, 2]
  • Operation: Push -1 (negative, so we can sum everything at the end)
  • New stack: [6, 2, -1]
  • Update: k = 0

Final Calculation:

  • Stack contains: [6, 2, -1]
  • Result: sum([6, 2, -1]) = 7

Verification: Let's verify by evaluating the original expression with proper precedence:

  • 5 * 4 / 3 + 2 - 1
  • First: 5 * 4 = 20, then 20 / 3 = 6
  • Then: 6 + 2 - 1 = 7

The stack approach correctly handles operator precedence by immediately evaluating multiplication and division (popping, computing, pushing back), while deferring addition and subtraction (just pushing values or their negatives) until the final sum.

Solution Implementation

1class Solution:
2    def clumsy(self, n: int) -> int:
3        """
4        Calculate the clumsy factorial of n.
5        Clumsy factorial: n * (n-1) / (n-2) + (n-3) - (n-4) * (n-5) / (n-6) + (n-7) - ...
6        Operations cycle through: multiply (*), divide (/), add (+), subtract (-)
7      
8        Args:
9            n: A positive integer
10          
11        Returns:
12            The result of the clumsy factorial calculation
13        """
14        # Initialize operation index (0: multiply, 1: divide, 2: add, 3: subtract)
15        operation_index = 0
16      
17        # Stack to store intermediate results
18        # Start with n as the first operand
19        stack = [n]
20      
21        # Process remaining numbers from n-1 down to 1
22        for current_num in range(n - 1, 0, -1):
23            if operation_index == 0:
24                # Multiply: pop last value, multiply with current number, push result
25                stack.append(stack.pop() * current_num)
26            elif operation_index == 1:
27                # Divide: pop last value, perform integer division, push result
28                # Using int() for truncation towards zero (Python's // floors for negative)
29                stack.append(int(stack.pop() / current_num))
30            elif operation_index == 2:
31                # Add: push positive number to stack
32                stack.append(current_num)
33            else:  # operation_index == 3
34                # Subtract: push negative number to stack (will be added in final sum)
35                stack.append(-current_num)
36          
37            # Cycle through operations (0, 1, 2, 3, 0, 1, ...)
38            operation_index = (operation_index + 1) % 4
39      
40        # Sum all values in the stack to get the final result
41        return sum(stack)
42
1class Solution {
2    /**
3     * Calculates the clumsy factorial of n.
4     * Clumsy factorial uses operations in the order: multiply (*), divide (/), add (+), subtract (-)
5     * rotating through these operations from left to right.
6     * Example: clumsy(10) = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1
7     * 
8     * @param n the input number to calculate clumsy factorial
9     * @return the result of the clumsy factorial calculation
10     */
11    public int clumsy(int n) {
12        // Stack to store intermediate results during calculation
13        Deque<Integer> stack = new ArrayDeque<>();
14      
15        // Push the first number onto the stack
16        stack.push(n);
17      
18        // Operation index: 0 = multiply, 1 = divide, 2 = add, 3 = subtract
19        int operationIndex = 0;
20      
21        // Process remaining numbers from n-1 down to 1
22        for (int currentNumber = n - 1; currentNumber > 0; currentNumber--) {
23            if (operationIndex == 0) {
24                // Multiply: pop the top value, multiply with current number, push result
25                stack.push(stack.pop() * currentNumber);
26            } else if (operationIndex == 1) {
27                // Divide: pop the top value, divide by current number, push result
28                stack.push(stack.pop() / currentNumber);
29            } else if (operationIndex == 2) {
30                // Add: push the current number as positive
31                stack.push(currentNumber);
32            } else {
33                // Subtract: push the current number as negative
34                stack.push(-currentNumber);
35            }
36          
37            // Cycle through operations: 0 -> 1 -> 2 -> 3 -> 0
38            operationIndex = (operationIndex + 1) % 4;
39        }
40      
41        // Sum all values in the stack to get the final result
42        return stack.stream().mapToInt(Integer::intValue).sum();
43    }
44}
45
1class Solution {
2public:
3    int clumsy(int n) {
4        // Stack to store intermediate results
5        stack<int> stk;
6      
7        // Push the first number onto the stack
8        stk.push(n);
9      
10        // Operation index: 0=multiply, 1=divide, 2=add, 3=subtract
11        int operationIndex = 0;
12      
13        // Process remaining numbers from n-1 down to 1
14        for (int currentNum = n - 1; currentNum > 0; --currentNum) {
15            if (operationIndex == 0) {
16                // Multiply: combine with top of stack
17                stk.top() *= currentNum;
18            } else if (operationIndex == 1) {
19                // Divide: combine with top of stack (integer division)
20                stk.top() /= currentNum;
21            } else if (operationIndex == 2) {
22                // Add: push positive number to stack
23                stk.push(currentNum);
24            } else {
25                // Subtract: push negative number to stack
26                stk.push(-currentNum);
27            }
28          
29            // Cycle through operations: *, /, +, -
30            operationIndex = (operationIndex + 1) % 4;
31        }
32      
33        // Calculate final result by summing all values in stack
34        int result = 0;
35        while (!stk.empty()) {
36            result += stk.top();
37            stk.pop();
38        }
39      
40        return result;
41    }
42};
43
1/**
2 * Calculates the clumsy factorial of a given number.
3 * Clumsy factorial uses operations in the order: multiply (*), divide (/), add (+), subtract (-)
4 * and applies them to consecutive decreasing integers.
5 * Example: clumsy(10) = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1
6 * 
7 * @param n - The input number to calculate clumsy factorial for
8 * @returns The result of the clumsy factorial calculation
9 */
10function clumsy(n: number): number {
11    // Stack to store intermediate results
12    const stack: number[] = [n];
13  
14    // Operation index tracker (0: multiply, 1: divide, 2: add, 3: subtract)
15    let operationIndex = 0;
16  
17    // Process each number from n-1 down to 1
18    for (let currentNumber = n - 1; currentNumber > 0; currentNumber--) {
19        if (operationIndex === 0) {
20            // Multiply: pop the last value and multiply with current number
21            const lastValue = stack.pop()!;
22            stack.push(lastValue * currentNumber);
23        } else if (operationIndex === 1) {
24            // Divide: pop the last value, divide by current number, and truncate to integer
25            const lastValue = stack.pop()!;
26            stack.push(Math.trunc(lastValue / currentNumber));
27        } else if (operationIndex === 2) {
28            // Add: push the current number as positive
29            stack.push(currentNumber);
30        } else {
31            // Subtract: push the current number as negative
32            stack.push(-currentNumber);
33        }
34      
35        // Cycle through operations (0, 1, 2, 3, 0, 1, ...)
36        operationIndex = (operationIndex + 1) % 4;
37    }
38  
39    // Sum all values in the stack to get the final result
40    return stack.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
41}
42

Time and Space Complexity

The time complexity is O(n) because the code iterates through a range from n-1 down to 1, which results in exactly n-1 iterations. In each iteration, the operations performed (arithmetic operations, stack append/pop) are all O(1) operations. Therefore, the overall time complexity is O(n-1) which simplifies to O(n).

The space complexity is O(n) because the stack stk can potentially store up to n elements in the worst case. Initially, the stack contains one element n, and then for each iteration of the loop, we either modify the last element (for multiplication and division operations when k=0 or k=1) or add a new element (for addition and subtraction operations when k=2 or k=3). In the pattern of operations (multiply, divide, add, subtract), half of the operations add new elements to the stack, so the stack can grow to approximately n/2 + 1 elements, which is O(n) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Division Operation

The Pitfall: Using Python's floor division operator // instead of truncation toward zero. This causes incorrect results for negative numbers.

# WRONG approach
stack.append(stack.pop() // current_num)

Why it's wrong:

  • Python's // performs floor division, which rounds toward negative infinity
  • For negative results: -7 // 2 = -4 (floors to -4)
  • The problem requires truncation toward zero: -7 / 2 = -3.5 → -3

The Solution:

# CORRECT approach
stack.append(int(stack.pop() / current_num))

This uses regular division followed by int() conversion, which truncates toward zero correctly.

2. Not Handling Operator Precedence Properly

The Pitfall: Trying to evaluate the expression left-to-right without respecting that multiplication and division have higher precedence than addition and subtraction.

# WRONG approach - evaluating sequentially
result = n
for i in range(n-1, 0, -1):
    if operation == '*':
        result = result * i
    elif operation == '/':
        result = int(result / i)
    elif operation == '+':
        result = result + i  # Wrong! This evaluates immediately
    elif operation == '-':
        result = result - i  # Wrong! This evaluates immediately

Why it's wrong:

  • This would calculate 10 * 9 / 8 + 7 as ((10 * 9) / 8) + 7 = 18
  • But we need to continue with multiplication/division first: 10 * 9 / 8 + 7 - 6 * 5 / 4
  • The 6 * 5 / 4 should be evaluated before adding to the previous result

The Solution: Use a stack to defer addition and subtraction operations:

  • Multiplication and division modify the top of the stack immediately
  • Addition pushes a new positive value to the stack
  • Subtraction pushes a new negative value to the stack
  • Final sum combines all values respecting precedence

3. Edge Case: Small Values of n

The Pitfall: Not considering that when n is very small (1, 2, or 3), the pattern doesn't complete a full cycle.

Examples:

  • n = 1: Result is simply 1 (no operations)
  • n = 2: Result is 2 * 1 = 2 (only multiplication)
  • n = 3: Result is 3 * 2 / 1 = 6 (multiply then divide)

The Solution: The stack-based approach naturally handles these cases without special logic:

  • The loop range(n-1, 0, -1) correctly handles when there are fewer operations
  • The operation index cycling ensures the right operations are applied in order
  • No special edge case handling needed!
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

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

Load More