Problem overview
The method tribonacci takes an Int named n and returns T(n), the n-th Tribonacci number. The sequence starts with T(0) = 0, T(1) = 1 and T(2) = 1. After that, every number is the sum of the three before it: T(n) = T(n - 1) + T(n - 2) + T(n - 3).
For n = 4, the answer is T(3) + T(2) + T(1). Since T(3) = 1 + 1 + 0 = 2, that gives 2 + 1 + 1 = 4. The first terms are 0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149.
Approach: keep only the last three values
The definition reads like a recursive function, and a direct translation works for small inputs. But each call spawns three more, and the same values are recomputed again and again, so the running time grows exponentially.
The statement asks for O(n) time and O(1) space. The key observation is that computing the next number needs only the three most recent ones. There is no reason to remember T(3) once you have reached T(7). So the loop carries three variables, computes one new number per iteration, and shifts the window forward by one position.
Step by step
- Handle the base cases.
T(0)andT(1)equal their own index, so whennis less than 2, returnnright away. - Declare the window.
prev2,prevandcurrstart asT(0),T(1)andT(2). They arevars because every iteration changes them.curris the newest value,previs one step behind andprev2two steps. - Loop from 3 to n. Each iteration produces one new term,
T(3)throughT(n). Forn = 2the loop does nothing, andcurr, which already holdsT(2), is the answer. - Compute the next term as the sum of the three variables. It is a
valbecause it is a new value in every pass and never reassigned. - Shift the window, oldest first. Copy
previntoprev2, thencurrintoprev, thennextintocurr. Each variable is overwritten only after its old value has been copied one place back. In the wrong order, a value would be lost before anyone used it. - Return
curr. After the last iteration it holdsT(n).
The solution in Scala
class Solution {
def tribonacci(n: Int): Int = {
if (n < 2) {
return n
}
var prev2 = 0
var prev = 1
var curr = 1
for (i <- 3 to n) {
val next = prev2 + prev + curr
prev2 = prev
prev = curr
curr = next
}
return curr
}
}
The Scala details worth knowing:
3 to nis inclusive. It includes both 3 andn. Whennis 2,3 to 2is an empty range, so the body never runs.3 until nwould stop one term early.for (i <- ...)is a generator. The loop variableiis a freshvalin each pass. Here it only counts iterations, and the body never reads it.valversusvar. Avalcan't be reassigned; avarcan. Usingvalfornextandvarfor the three window variables tells the reader which names change over time.returnis optional. The last expression of a block is its value, so the final line could be justcurr. Scala programmers usually write that. The explicitreturnin the lesson works the same way. A method that usesreturnneeds its result type written out, which: Intdoes here.Intis 32 bits. Scala does not warn when a sum goes past the limit; the value silently wraps around. This matters for largen, as described below.
A worked trace
Take n = 5. Before the loop, prev2, prev and curr are 0, 1 and 1.
i = 3:nextis 0 + 1 + 1 = 2, and the window becomes 1, 1, 2.i = 4:nextis 1 + 1 + 2 = 4, and the window becomes 1, 2, 4.i = 5:nextis 1 + 2 + 4 = 7, and the window becomes 2, 4, 7.
The loop ends, and curr is 7, which is T(5). The oldest value drops out of the window on every pass, which is why three variables are enough no matter how large n is.
Complexity
Time: O(n). The loop runs n - 2 times and each pass does a fixed number of additions and assignments.
Space: O(1). The method uses four local variables whatever the size of n. A recursive version would need a call stack as deep as n, and a memoised one would store n values.
Edge cases and common mistakes
- Small
n.tribonacci(0)returns 0,tribonacci(1)returns 1 andtribonacci(2)returns 1. The last one comes from the initial value ofcurrwith an empty loop. - Overflow.
T(37)is 2082876103, the largest term that fits in anInt.tribonacci(38)returns -463960867, because the true value is above 2147483647 and the addition wraps. If larger inputs mattered, the variables and the return type would beLong, which holds terms up to a much largern. - Shifting in the wrong order. If
curr = nextcomes first, thenprev = currcopies the new value and the window collapses into duplicates. - Using
until.for (i <- 3 until n)computes only up toT(n - 1), so every answer is one term short. - Off-by-one starting values. The variables must start at
T(0),T(1)andT(2). Startingcurrat 0 shifts the whole sequence.
Testing
n = 3gives2.n = 4gives4.n = 5gives7.n = 10gives149.n = 0gives0, andn = 2gives1.
