Start learning free

LESSON 167 OF 348 · LOOPS - CHALLENGES

Tribonacci Number in TypeScript: O(n) Time, O(1) Space

Practice Read the full explanation

The Tribonacci numbers, commonly denoted T(n) form a sequence, called the Tribonacci sequence, such that each number is the sum of the three preceding ones, starting from 0, 1 and 1. That is,

T(0) = 0, T(1) = 1, T(2) = 1
T(n) = T(n - 1) + T(n - 2) + T(n - 3), for n > 2.

Given n, calculate and return T(n).


Example 1:

Input: n = 3
Output: 2
Explanation: T(3) = T(2) + T(1) + T(0) = 1 + 1 + 0 = 2.

Example 2:

Input: n = 4
Output: 4
Explanation: T(4) = T(3) + T(2) + T(1) = 2 + 1 + 1 = 4.

Example 3:

Input: n = 5
Output: 7
Explanation: T(5) = T(4) + T(3) + T(2) = 4 + 2 + 1 = 7.

Note:

Your algorithm should run in O(n) time and use O(1) space.


STEP 1 OF 8
    Ctrl + Enter to check

    TYPESCRIPT · LOOPS - CHALLENGES

    Tribonacci in TypeScript, explained

    Problem overview

    The method tribonacci(n) returns T(n). The Tribonacci sequence starts with T(0) = 0, T(1) = 1, T(2) = 1, and each later number is the sum of the three before it: T(n) = T(n - 1) + T(n - 2) + T(n - 3). The values run 0, 1, 1, 2, 4, 7, 13, 24.

    In the examples, T(3) = 1 + 1 + 0 = 2, T(4) = 2 + 1 + 1 = 4 and T(5) = 4 + 2 + 1 = 7. The statement asks for O(n) time and O(1) space.

    Approach: slide a window of three

    This is the Fibonacci idea with one more value. Each number depends only on the three before it, so you never need the whole sequence: three variables are enough. Name them by how old they are. curr is the newest value, prev is one step behind and prev2 is two steps behind. Start them at T(2), T(1) and T(0), then repeat: compute the next number as the sum of the three, and shift each variable one place back.

    A direct recursive version would call itself three times for every value and repeat the same computations many times, so its running time grows exponentially. The loop computes each number once.

    Counting the calls of a plain recursive version makes the difference concrete: it makes 289 calls for n = 10, 128,287 for n = 20 and 56,843,233 for n = 30. The loop does 8, 18 and 28 additions for the same inputs.

    One detail makes the early return work. For n = 0 and n = 1 the answer is the input itself, but n = 2 gives 1 as well, and 2 is not equal to 1. That is why the check is n < 2 and not n < 3: the value for n = 2 comes from the starting value of curr, not from the early return.

    Fibonacci with a bigger window

    The only differences from the two-variable Fibonacci loop are the number of variables and the starting point. With two values the loop starts at 2 and the window is F(i - 2) and F(i - 1). With three values the loop starts at 3, because the three starting numbers T(0), T(1) and T(2) are already in the window. The same idea works for any fixed window size: hold the last k values, add them, and shift. It stays O(1) in space for any constant k. A version that stored the whole sequence in an array would also be O(n) in time but would need O(n) space, which the statement rules out.

    Step by step

    1. Handle n < 2. T(0) = 0 and T(1) = 1 equal n, so return n for them.
    2. Declare the three variables in a single let, separated by commas: prev2 = 0, prev = 1, curr = 1. They hold T(0), T(1) and T(2). For n = 2 the loop below doesn't run and curr, which is T(2), is returned directly.
    3. Loop from 3 to n, with i <= n so that n itself is computed.
    4. Compute next as prev2 + prev + curr.
    5. Shift the values, oldest first. Assign prev to prev2, then curr to prev, then next to curr. Each variable is overwritten only after its old value has been copied one place back.
    6. Return curr after the loop.

    Trace n = 5. The state starts as (prev2, prev, curr) = (0, 1, 1). At i = 3, next = 2, and the state becomes (1, 1, 2). At i = 4, next = 4 and the state becomes (1, 2, 4). At i = 5, next = 7 and the state becomes (2, 4, 7). The method returns 7.

    The solution in TypeScript

    class Solution {
        tribonacci(n: number): number {
            if (n < 2) {
                return n;
            }
            let prev2 = 0, prev = 1, curr = 1;
            for (let i = 3; i <= n; i++) {
                let next = prev2 + prev + curr;
                prev2 = prev;
                prev = curr;
                curr = next;
            }
            return curr;
        }
    }

    The three variables are declared together and inferred as number. The three shifting assignments are separate statements run in order, and they can't be reordered: if you wrote prev = curr before prev2 = prev, the old prev would be lost. JavaScript also lets you swap with destructuring, [prev2, prev, curr] = [prev, curr, next], which does all three at once, but the plain assignments make the order visible.

    All numbers in TypeScript are doubles. The results are exact only while they stay below 2^53. The Tribonacci numbers grow faster than the Fibonacci numbers, and tribonacci(63) is already beyond the safe-integer range, so the result can no longer be trusted from there. The lesson's inputs are small enough not to reach it.

    Complexity

    The loop runs n - 2 times and does a constant amount of work each time, so the time is O(n). The method stores four numbers, prev2, prev, curr and next, plus the index, so the extra space is O(1).

    Edge cases and common mistakes

    • Small n. n = 0 and n = 1 return n through the early check. n = 2 skips the loop and returns the starting curr, which is 1.
    • Starting at the wrong values. The sequence starts 0, 1, 1, unlike a version that starts 0, 0, 1. Make sure curr begins as 1.
    • Starting the loop at 2. That would compute T(2) again and produce a value one step too far.
    • Shifting in the wrong order. Updating curr first leaves nothing to copy into prev.
    • Stopping at i < n. The method would return T(n - 1).
    • Negative or fractional n. The lesson promises a non-negative integer. A negative n would pass the early check and be returned unchanged, which is not a sequence value, and the number type does not stop a caller from passing it. Validate the input if the method is used outside the lesson.

    Testing

    Try n = 3 (2), n = 4 (4), n = 10 (149) and n = 2 (1).

    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free