1006. Clumsy Factorial
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:
- Multiply
*
- Divide
/
(using floor division) - Add
+
- 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
, then90 / 8 = 11
- Then calculate:
6 * 5 = 30
, then30 / 4 = 7
- Then calculate:
2 * 1 = 2
- Finally combine:
11 + 7 - 7 + 3 - 2 = 12
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:
- When we encounter multiplication or division, we need to immediately evaluate it with the previous number (since these have higher precedence)
- 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.
Solution Approach
We implement the solution using a stack and simulation approach.
Initialization:
- Create a stack
stk
and push the initial valuen
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
:
-
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
-
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
-
When
k = 2
(Addition):- Simply push
x
onto the stack - We don't need to perform the addition immediately since it has lower precedence
- Simply push
-
When
k = 3
(Subtraction):- Push
-x
onto the stack (negative value) - By storing the negative, we can sum all values at the end
- Push
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 EvaluatorExample 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
, then20 / 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 simply1
(no operations)n = 2
: Result is2 * 1 = 2
(only multiplication)n = 3
: Result is3 * 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!
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
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!