Problem overview
The method factorial receives a non-negative integer n and returns n!, the product of all whole numbers from 1 to n. For n = 5 the answer is 1 * 2 * 3 * 4 * 5 = 120.
The lesson asks for O(n) time and O(1) extra space. One loop and one running product are enough to meet both limits.
Approach: build the product one factor at a time
Start with a product of 1 and fold in each factor in turn. For n = 4 the product goes 1, 2, 6, 24: after multiplying by i, it holds i!. A loop from 1 to n repeats the same multiplication, so the code never needs to know n in advance.
The starting value of 1 matters. It is the identity for multiplication, so it changes nothing on the first step, and it is also the correct answer for n = 0, because 0! is defined as 1 and the loop then has nothing to do.
Traced for n = 5: fact starts at 1, then the loop multiplies it by 1, 2, 3, 4 and 5 in turn, giving 1, 2, 6, 24 and finally 120. The multiplication by 1 on the first pass is harmless, so starting the loop at 1 is fine.
Step by step
- Declare the running product. Call it
fact, make it avarbecause it changes on every pass, and start it at 1. Swift infers the typeIntfrom the literal. - Loop over the factors. Use
iover the half-open range1..<(n + 1), which covers 1 up to and includingn. - Multiply inside the loop.
fact *= ifolds the current factor into the product. - Return
factafter the loop, when every factor has been used.
The solution in Swift
class Solution {
func factorial(_ n: Int) -> Int {
var fact = 1
for i in 1..<(n + 1) {
fact *= i
}
return fact
}
}
Swift details worth knowing:
- Two range operators.
a..<bstops beforeb, anda...bincludesb. Writing1...nlooks more natural, but a closed range needs its lower bound to be at most its upper bound. Withn = 0the range1...0is invalid, and the program crashes at run time. The half-open1..<1is just empty, which is why the solution writes1..<(n + 1). varversuslet. Aletconstant can't change. Declaringfactwithletmakesfact *= ia compile error: the left side of the mutating operator isn't mutable.- Overflow traps.
Intis 64 bits. Arithmetic that doesn't fit doesn't wrap around: the program stops with a crash.factorial(20)returns 2432902008176640000, which fits, butfactorial(21)would exceed the largestIntand crash. - The label
_. The parameter is declared_ n, so callers writefactorial(5)without a label.
Complexity
Time: O(n). The loop runs exactly n times and each pass does one multiplication.
Space: O(1). The function keeps only fact and the loop variable, whatever the size of n.
Edge cases and common mistakes
n = 0. The loop doesn't run and the function returns 1, which is correct.n = 1. The loop multiplies by 1 once and the result is 1.- Starting at 0. A product that starts at 0 stays 0 forever.
- Stopping one factor early.
1..<nleaves outnitself, sofactorial(5)would give 24 instead of 120. - Returning inside the loop. The function would end after the first factor.
- Large inputs. Any
nabove 20 overflowsIntand crashes.
Testing
n = 0gives1.n = 1gives1.n = 5gives120.n = 10gives3628800.n = 20gives2432902008176640000.
