Problem overview
The method fibonacci(n) returns F(n), the n-th Fibonacci number. The sequence starts F(0) = 0, F(1) = 1, and from then on each number is the sum of the two before it: F(n) = F(n - 1) + F(n - 2). The first values are 0, 1, 1, 2, 3, 5, 8, 13.
In the examples, F(2) = 1 + 0 = 1, F(3) = 1 + 1 = 2 and F(4) = 2 + 1 = 3. The statement asks for O(n) time and O(1) space.
Approach: keep only the last two numbers
The definition suggests recursion: to get F(n), compute F(n - 1) and F(n - 2). Written directly, that recalculates the same values over and over, and the number of calls grows exponentially. The sequence also gives a much simpler path. Each value depends only on the two before it, so there is no reason to store the whole sequence or to start from n and go down.
Start from the bottom instead. Keep two variables, prev and curr, holding F(0) and F(1). Then repeat the same step n - 1 times: compute the next number as curr + prev, and slide the window forward so that prev takes the old curr and curr takes the new number. When the loop ends, curr is F(n).
Why the direct recursion is too slow
Here is the definition written as a recursive function:
function fibRec(n: number): number {
if (n < 2) return n;
return fibRec(n - 1) + fibRec(n - 2);
}
It is correct, but fibRec(5) calls fibRec(3) twice, fibRec(2) three times, and so on. Counting the calls shows what happens: 177 calls for n = 10, 21,891 for n = 20 and 2,692,537 for n = 30. Each step up in n multiplies the work by roughly 1.6. The loop avoids all of it by computing each value once, from the bottom, and keeping only what the next step needs. This idea of building up from small cases is the core of dynamic programming.
Why not store the whole sequence?
A common first attempt fills an array: fib[0] = 0, fib[1] = 1, then fib[i] = fib[i - 1] + fib[i - 2] for each i up to n, and returns fib[n]. It runs in O(n) time as well and is easy to follow, but it keeps n + 1 numbers when only two are ever read again. The statement asks for O(1) space, so the two-variable window is the version to write here. Recognising that a result depends on a fixed number of recent values, and keeping only those, is a pattern you will meet again.
Step by step
- Handle the base cases. If
n < 2, returnn:F(0)is 0 andF(1)is 1, so the input is its own answer. This also covers both starting values with one check. - Declare
prevandcurras 0 and 1. At the start of each iteration fori,prevholdsF(i - 2)andcurrholdsF(i - 1). - Loop with
ifrom 2 ton, inclusive, becauseF(2)throughF(n)are the values still to compute. - Compute
nextascurr + prev. This isF(i). - Slide the window. Assign
currtoprevfirst, thennexttocurr. The order matters: reversing it would overwritecurrbefore its old value was saved. Check withprev = 1andcurr = 2: afterwardsprev = 2andcurr = 3. - Return
currafter the loop.
Take n = 4. The state starts as prev = 0, curr = 1. At i = 2, next = 1, so the state becomes prev = 1, curr = 1. At i = 3, next = 2 and the state becomes prev = 1, curr = 2. At i = 4, next = 3 and the state becomes prev = 2, curr = 3. The method returns 3.
The solution in TypeScript
class Solution {
fibonacci(n: number): number {
if (n < 2) {
return n;
}
let prev = 0, curr = 1;
for (let i = 2; i <= n; i++) {
let next = curr + prev;
prev = curr;
curr = next;
}
return curr;
}
}
One let declares both prev and curr, separated by a comma. The compiler infers their type as number from the initial values, so no annotation is needed. next is declared inside the loop body, so each iteration gets a fresh variable.
TypeScript has one numeric type, number, which is a double-precision float. It has no integer overflow, but it stops being exact for integers beyond 2^53. For this method, the results are exact up to F(78). From F(79) on the value is no longer a safe integer: fibonacci(79) gives 14472334024676220, while the true value ends in 221. The lesson's inputs stay well below that range. Far larger inputs, around n = 1477, make the result overflow to Infinity.
Complexity
The loop runs n - 1 times with constant work in each iteration, so the time is O(n). Only prev, curr, next and i are stored, so the extra space is O(1). The recursive definition, by contrast, makes an exponential number of calls without caching.
Edge cases and common mistakes
n = 0andn = 1. Without the early return, the loop would be skipped andcurrwould be 1, which is wrong forF(0).n = 2. The loop runs once and returns 1.- Swapping in the wrong order. Assigning
prev = curraftercurr = nextwould set both to the new value and break the sequence. - Starting the loop at 1 or stopping at
i < n. Either way the loop runs the wrong number of times and returns a neighbouring Fibonacci number. - Negative or fractional
n. The lesson promises a non-negative integer. A negativenwould pass the early check and be returned unchanged, which is not a sequence value, and thenumbertype does not stop a caller from passing it. Validate the input if the method is used outside the lesson.
Testing
Try n = 0 (0), n = 1 (1), n = 10 (55) and n = 50 (12586269025).