Problem overview
The method factorial receives a non-negative integer n and returns n!, the product 1 * 2 * 3 * ... * n. For n = 5 that is 1 * 2 * 3 * 4 * 5 = 120.
The statement asks for O(n) time and O(1) extra space. A single loop with one running product meets both limits.
Approach: multiply as you count
Build the product one factor at a time. Start with a variable holding 1, then walk through the numbers 1, 2, ..., n and multiply each into it. After the factor 2 the variable holds 2, after 3 it holds 6, and after 4 it holds 24. The last multiplication gives the answer.
The variable must start at 1, not 0. Multiplying by 1 changes nothing, so 1 is the neutral starting value for a product, just as 0 is for a sum. Starting at 0 would keep the product at 0 forever.
Step by step
- Declare
factas anumberstarting at 1. It holds the product so far and is declared withletbecause it changes on every pass. - Loop with
ifrom 1 throughn. The conditioni <= nmakes sure thatnitself is one of the factors. Each integer in that range is visited once. - Multiply inside the loop.
fact *= ireplacesfactwithfact * i, adding one more factor to the product. - Return
factafter the loop, once all the factors are in.
The solution in TypeScript
class Solution {
factorial(n: number): number {
let fact: number = 1;
for (let i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
}
Notes on the TypeScript side:
- One number type. TypeScript has no separate integer type, so
fact,iand the result are allnumber, a double-precision float. There is no integer overflow, but there is a limit on exactness, described below. - Type annotations disappear. The
: numbermarkers help the compiler and your editor. They are removed when the code is compiled to JavaScript, and they do not convert anything while the program runs. - The loop variable is local.
let iexists only inside theforstatement. - No recursion needed. A recursive version,
n * factorial(n - 1), is also correct, but it uses a stack frame per call and soO(n)space. The loop keeps the space constant.
Complexity
Time: O(n). The loop runs exactly n times, with one multiplication per pass.
Space: O(1). Only fact and i are stored, however large n is.
Edge cases and common mistakes
n = 0. By definition 0! is 1. The loop does not run, and the method returns the starting value,1, which is correct. This is one more reason to startfactat 1.n = 1. The loop runs once, multiplying 1 by 1, and the result is1.- Starting
factat 0. Every product would be 0. - Using
<instead of<=. The factornwould be missing, sofactorial(5)would return 24. - Large results. Factorials grow very fast.
factorial(18)is6402373705728000, which anumberholds exactly, but integers above 2^53 (about 9 * 10^15) are not all representable. Forfactorial(22)the method returns1.1240007277776077e+21, a rounded value in exponent notation. Atfactorial(171)the product is too big for anynumberand the result isInfinity, whilefactorial(170)still gives about7.257415615307994e+306. When exact huge factorials matter, JavaScript'sBigInttype is the tool for it.
Testing
factorial(5)gives120.factorial(0)gives1.factorial(1)gives1.factorial(10)gives3628800.factorial(20)gives2432902008176640000.