Problem overview
The function tribonacci takes an i32 called n and returns T(n). The sequence starts with T(0) = 0, T(1) = 1 and T(2) = 1, and from then on each number is the sum of the three before it: T(n) = T(n - 1) + T(n - 2) + T(n - 3).
The sequence runs 0, 1, 1, 2, 4, 7, 13, 24, and so on. For n = 3 the answer is 2 (1 + 1 + 0). For n = 4 it is 4 (2 + 1 + 1), and for n = 5 it is 7 (4 + 2 + 1). The statement asks for O(n) time and O(1) extra space.
Approach: slide a window of three numbers
Tribonacci is the Fibonacci idea with one more term. A direct recursive version would call itself three times per step and recompute the same values over and over, so it gets slow quickly. Building the sequence upward avoids that.
To compute any term you need only the three terms just before it. So keep exactly three values, named by how far behind the newest value they are: curr is the newest, prev is one step behind and prev2 is two steps behind. Start them at T(0), T(1) and T(2), then repeat: add the three, shift each value one place back, and put the sum in the newest slot. After computing T(n), the newest slot holds the answer.
Step by step
- Handle
n < 2first.T(0) = 0andT(1) = 1are their own answers, so returnnwhen it is less than 2. - Declare three
i32variables withlet mut:prev2 = 0,prev = 1andcurr = 1. They holdT(0),T(1)andT(2). All three change inside the loop, so all three needmut. - Loop over
3..=n. The terms still to compute areT(3)throughT(n). The loop variable is named_because the counter is never read. Forn = 2the range is empty, the loop runs zero times, andcurr, which is alreadyT(2), is returned as it is. - Compute
nextasprev2 + prev + curr. It is created with a plainlet, since each iteration makes a fresh one. - Shift the window, oldest first. Assign
prevtoprev2, thencurrtoprev, thennexttocurr. Each variable is overwritten only after its old value has been copied one place back. Doing it in the opposite order would destroy values before they are used. - Return
curras the last expression of the function, without a semicolon.
Tracing n = 5: the window starts as (0, 1, 1). For T(3) the sum is 2 and the window becomes (1, 1, 2). For T(4) the sum is 4 and the window becomes (1, 2, 4). For T(5) the sum is 7 and the window becomes (2, 4, 7). The function returns 7.
The solution in Rust
impl Solution {
fn tribonacci(n: i32) -> i32 {
if n < 2 {
return n;
}
let mut prev2: i32 = 0;
let mut prev: i32 = 1;
let mut curr: i32 = 1;
for _ in 3..=n {
let next = prev2 + prev + curr;
prev2 = prev;
prev = curr;
curr = next;
}
curr
}
}
Rust details worth knowing:
- The last expression is the return value.
currhas no semicolon, so it is the value of the function body. Adding a semicolon would turn it into a statement, the function would then return(), and the compiler would report a type mismatch. The earlyreturn n;inside theifdoes need the keyword, because it leaves the function from the middle. - The
_pattern. It accepts each value of the range and throws it away. It tells the reader, and the compiler, that the counter is intentionally unused, so there is no warning. - Inclusive ranges.
3..=nincludesn. The half-open3..nwould stop one term early. - Type inference.
nexthas no annotation. The compiler infersi32from the threei32values it adds. - Integers are copied.
prev2 = prevcopies ani32, so each assignment is independent and nothing is moved.
Complexity
Time: O(n). The loop runs n - 2 times for n of at least 3. Each pass does one three-way addition and three assignments.
Space: O(1). The function keeps four integers (prev2, prev, curr and next), whatever n is.
Edge cases and common mistakes
- The three base values.
tribonacci(0)is 0,tribonacci(1)is 1 andtribonacci(2)is 1. The first two come from the early return and the third fromcurr's starting value. - Starting the loop at 2.
for _ in 2..=nadds one term too many, sotribonacci(2)would return 2 instead of 1. - Wrong shift order. Assigning
curr = nextbeforeprev = currmakesprevandcurrequal, and the sequence goes wrong from the next term on. - Overflow.
T(37)is 2,082,876,103, the last term that fits in ani32.T(38)is 3,831,006,429, which doesn't. The lessons build Rust with optimizations, where integer overflow wraps silently, sotribonacci(38)returns -463960867. A debug build would panic with an overflow error instead. A wider type such asi64oru64moves the limit further out. - Allocating a vector. Storing the whole sequence in a
Vecgives the right answers, but usesO(n)space instead ofO(1).
Testing
n = 0gives0.n = 1gives1.n = 2gives1.n = 3gives2.n = 4gives4.n = 5gives7.n = 10gives149.n = 25gives1389537.
