Problem overview
The method factorial receives a non-negative integer n and returns n!, the product 1 * 2 * 3 * ... * n. For n = 5 the product is 1 * 2 * 3 * 4 * 5 = 120. By convention 0! is 1, because an empty product is 1.
The statement asks for O(n) time and O(1) extra space.
Approach: a running product
You can compute the result without any collection or recursion. Keep one variable, start it at 1, and multiply it by each number from 1 to n. After the multiplication by k, the variable holds k!. For n = 4 it goes through 1, 2, 6 and 24.
This is the same accumulator pattern as a running sum, with two differences: the operation is multiplication, and the starting value is 1, not 0. Multiplying by 1 changes nothing, so 1 is the neutral start. Starting at 0 would leave the product at 0 forever.
The loop makes n multiplications and uses a fixed number of variables, which meets both limits in the statement.
A recursive definition, n! = n * (n - 1)!, is also correct, but it keeps one call on the stack for each step and would need O(n) space. The loop is what makes the O(1) space limit possible.
A small hand check helps before you type anything. For n = 3, the variable goes 1, then 1 * 1 = 1, then 1 * 2 = 2, then 2 * 3 = 6. If your trace agrees with the definition, the loop is right.
Step by step
- Declare
factwithvarand set it to 1. The loop updates it, so it can't be aval. - Loop with
for (i <- 1 to n). The range1 to nincludes both ends, so every factor ofn!is visited exactly once. Forn = 0the range is empty. - Multiply with
fact *= i. This is shorthand forfact = fact * i. - Return
fact. The method returns only after the loop has finished, so the value is complete.
The solution in Scala
class Solution {
def factorial(n: Int): Int = {
var fact = 1
for (i <- 1 to n) {
fact *= i
}
return fact
}
}
Tracing n = 5:
i | fact afterwards |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 6 |
| 4 | 24 |
| 5 | 120 |
Language details
- Scala infers
Intforfactfrom the literal1. The method's return type is alsoInt. 1 to nincludesn.1 until nwould stop one short and drop the last factor.- The compound assignment
*=only works on avar. On avalthe compiler rejects it, because avalcan't be reassigned.
Complexity
The loop body runs n times and each run is one multiplication, so the time is O(n). The method keeps fact and the loop variable and nothing that grows with n, so the extra space is O(1).
Edge cases and common mistakes
n = 0. The loop doesn't run and the method returns the starting value 1, which is the right answer for0!.- Starting
factat 0. Every product is then 0. - Int overflow. Factorials grow very fast, and an
Intis 32 bits.factorial(12)returns 479001600, the largest factorial that fits.factorial(13)returns 1932053504, which is wrong: the true value is 6227020800.factorial(20)returns -2102132736. Scala does not raise an error on overflow, it wraps silently. ALongwould extend the range up to20!. - Returning inside the loop. A
returnin the loop body would end the method after the first pass.
Testing
Small inputs worth trying: 0 gives 1, 1 gives 1, 2 gives 2, 5 gives 120 and 10 gives 3628800.
