Problem overview
The function fibonacci takes an i32 called n and returns F(n), the n-th Fibonacci number. The sequence starts F(0) = 0 and F(1) = 1, and every later number is the sum of the two before it: F(n) = F(n - 1) + F(n - 2).
So the sequence runs 0, 1, 1, 2, 3, 5, 8, 13 and so on. For n = 2 the answer is 1, for n = 3 it is 2, and for n = 4 it is 3. The statement asks for O(n) time and O(1) extra space.
Approach: remember only the last two numbers
The definition reads like a recursive function, and writing it that way is the obvious first attempt. It is slow, though: F(n) calls F(n - 1) and F(n - 2), which both call their own smaller versions, so the same values are recomputed again and again and the work roughly multiplies with each step up in n.
To see the cost, count the calls of a recursive version: computing F(30) makes 2,692,537 calls to produce an answer of 832,040. A loop needs 29 additions for the same input.
The faster idea is to build the sequence from the bottom. To get F(i) you need only F(i - 1) and F(i - 2), and nothing older. So two variables, prev and curr, are enough. Start from F(0) and F(1) and slide this two-number window forward one position at a time until curr holds F(n). That does n - 1 additions and stores two integers, which meets the O(n) and O(1) budget.
Step by step
- Handle the base cases. If
nis less than 2, returnnitself:F(0)is 0 andF(1)is 1. One comparison covers both. - Declare
prevandcurraslet mutvariables of typei32, set to 0 and 1. At the start they holdF(0)andF(1). At the start of each iteration for indexi,prevholdsF(i - 2)andcurrholdsF(i - 1). - Loop from 2 to
ninclusive with2..=n. The numbers still missing areF(2)throughF(n), and the=makes the range includen. - Compute
next, the sum ofcurrandprev. This isF(i). - Slide the window. Assign
currtoprevfirst, thennexttocurr. The order matters: the oldcurrmust be copied intoprevbeforecurris overwritten. Starting fromprev = 1andcurr = 2, the update givesprev = 2andcurr = 3. - Return
curr. After the last iteration it holdsF(n).
Tracing n = 4: the loop starts with prev = 0 and curr = 1. For i = 2 the new value is 1, giving prev = 1 and curr = 1. For i = 3 it is 2, giving prev = 1 and curr = 2. For i = 4 it is 3, giving prev = 2 and curr = 3. The function returns 3.
The solution in Rust
impl Solution {
fn fibonacci(n: i32) -> i32 {
if n < 2 {
return n;
}
let mut prev: i32 = 0;
let mut curr: i32 = 1;
for i in 2..=n {
let next: i32 = curr + prev;
prev = curr;
curr = next;
}
return curr;
}
}
Rust details worth knowing:
- Bindings are immutable by default.
prevandcurrchange on every iteration, so they needmut.nextis created fresh in each iteration and never changes, so a plainletis right. - Inclusive and exclusive ranges.
2..nstops beforen, while2..=nincludes it. Using the wrong one makes the answerF(n - 1). Whennis 2 the range2..=2holds one value, so the loop runs once. - The loop variable is never read. The loop only counts iterations, so the compiler warns that
iis unused. Writing_instead ofiwould silence it. The code is correct either way. - Copying is cheap.
prev = currcopies ani32; there is no ownership issue because integers implementCopy. - Early
return. Thereturn n;inside theifleaves the function immediately. The finalreturn curr;could also be written as justcurr, with no semicolon.
Complexity
Time: O(n). The loop runs n - 1 times and each iteration does a fixed amount of work: one addition and two assignments. Doubling n doubles the number of iterations, which is what linear time means. The loop is also much faster than the recursive version, because every Fibonacci number is computed exactly once and then reused.
Space: O(1). Only prev, curr, next and the loop counter exist, however large n is.
Edge cases and common mistakes
- Zero and one.
fibonacci(0)is 0 andfibonacci(1)is 1. Without the early return, the loop would be skipped for these inputs and the function would wrongly return 1 forn = 0. - Overflow.
F(46)is 1,836,311,903, the largest Fibonacci number that fits in ani32.F(47)would be 2,971,215,073, which doesn't fit. The lessons build Rust with optimizations, where integer overflow wraps silently, sofibonacci(47)returns -1323752223. A debug build would panic with an overflow error instead. Larger return types such asi64oru64push the limit further out. - Wrong update order. Writing
curr = next;beforeprev = curr;makesprevandcurrequal, and the sequence stops growing correctly. - Starting the loop at 1. With
for i in 1..=nthe loop runs one time too many and the result becomesF(n + 1). Forn = 4it would return 5 instead of 3. - Negative input. Since
n < 2catches every negative value,fibonacci(-3)returns -3. Fibonacci numbers aren't defined here for negativen, so no particular result is expected.
Testing
n = 0gives0.n = 1gives1.n = 2gives1.n = 3gives2.n = 4gives3.n = 10gives55.n = 46gives1836311903.
