Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Recursion - Understanding how recursive functions call themselves with smaller subproblems
Dynamic Programming Basics - Recognizing overlapping subproblems and optimal substructure
Memoization - Caching computed results to avoid redundant calculations in recursive solutions
1. Recursion
Intuition
The tribonacci sequence is defined similarly to Fibonacci, but instead of summing the previous two numbers, we sum the previous three. The base cases are T(0) = 0, T(1) = 1, and T(2) = 1. For any n >= 3, we compute T(n) = T(n-1) + T(n-2) + T(n-3).
A straightforward recursive approach directly implements this definition. However, this leads to exponential time complexity because we recompute the same subproblems many times. Each call branches into three more calls, creating a tree with many redundant calculations.
Algorithm
If n is 0, return 0. If n is 1 or 2, return 1.
Otherwise, recursively compute tribonacci(n-1), tribonacci(n-2), and tribonacci(n-3).
functribonacci(n int)int{if n <=2{if n ==0{return0}return1}returntribonacci(n-1)+tribonacci(n-2)+tribonacci(n-3)}
class Solution {funtribonacci(n: Int): Int {if(n ==0)return0if(n <=2)return1returntribonacci(n -1)+tribonacci(n -2)+tribonacci(n -3)}}
classSolution{functribonacci(_ n:Int)->Int{if n ==0{return0}if n <=2{return1}returntribonacci(n -1)+tribonacci(n -2)+tribonacci(n -3)}}
Time & Space Complexity
Time complexity: O(3n)
Space complexity: O(n)
2. Dynamic Programming (Top-Down)
Intuition
The recursive solution wastes time by recomputing the same values over and over. We can fix this with memoization: store each computed result in a cache. Before computing T(n), check if it already exists in the cache. If so, return the cached value immediately. This transforms our exponential algorithm into a linear one, since each unique subproblem is solved only once.
Algorithm
Create a dp hash map or dictionary to store computed values.
If n is 0, return 0. If n is 1 or 2, return 1.
If n is already in the dp cache, return the cached value.
Otherwise, compute tribonacci(n-1) + tribonacci(n-2) + tribonacci(n-3), store it in the dp cache, and return it.
classSolution:def__init__(self):
self.dp ={}deftribonacci(self, n:int)->int:if n <=2:return1if n !=0else0if n in self.dp:return self.dp[n]
self.dp[n]= self.tribonacci(n -1)+ self.tribonacci(n -2)+ self.tribonacci(n -3)return self.dp[n]
functribonacci(n int)int{
dp :=make(map[int]int)var helper func(n int)int
helper =func(n int)int{if n ==0{return0}if n <=2{return1}if val, ok := dp[n]; ok {return val
}
dp[n]=helper(n-1)+helper(n-2)+helper(n-3)return dp[n]}returnhelper(n)}
class Solution {privateval dp = HashMap<Int, Int>()funtribonacci(n: Int): Int {if(n ==0)return0if(n <=2)return1if(dp.containsKey(n))return dp[n]!!
dp[n]=tribonacci(n -1)+tribonacci(n -2)+tribonacci(n -3)return dp[n]!!}}
classSolution{privatevar dp =[Int:Int]()functribonacci(_ n:Int)->Int{if n ==0{return0}if n <=2{return1}iflet val = dp[n]{return val }
dp[n]=tribonacci(n -1)+tribonacci(n -2)+tribonacci(n -3)return dp[n]!}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Dynamic Programming (Bottom-Up)
Intuition
Instead of working backwards from n and relying on recursion, we can build up the solution from the base cases. Start with the known values T(0), T(1), and T(2), then iteratively compute each subsequent value until we reach T(n). This eliminates recursion overhead and makes the computation straightforward.
Algorithm
If n is 0, return 0. If n is 1 or 2, return 1.
Create an array dp of size n+1 and initialize dp[0] = 0, dp[1] = 1, dp[2] = 1.
For each i from 3 to n, compute dp[i] = dp[i-1] + dp[i-2] + dp[i-3].
classSolution{functribonacci(_ n:Int)->Int{if n ==0{return0}if n <=2{return1}var dp =[Int](repeating:0, count: n +1)
dp[1]=1
dp[2]=1for i in3...n {
dp[i]= dp[i -1]+ dp[i -2]+ dp[i -3]}return dp[n]}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
4. Dynamic Programming (Space Optimized)
Intuition
Notice that to compute T(n), we only need the three most recent values: T(n-1), T(n-2), and T(n-3). We do not need to store the entire history. By keeping just three variables (or a small fixed-size array), we can reduce space complexity from O(n) to O(1).
A clever trick uses an array of size 3 and the modulo operator. At iteration i, we store the new value at index i % 3, which naturally overwrites the oldest value we no longer need.
Algorithm
Initialize an array t = [0, 1, 1] representing the first three tribonacci numbers.
If n < 3, return t[n] directly.
For each i from 3 to n, compute t[i % 3] = t[0] + t[1] + t[2].
classSolution:deftribonacci(self, n:int)->int:
t =[0,1,1]if n <3:return t[n]for i inrange(3, n +1):
t[i %3]=sum(t)return t[n %3]
publicclassSolution{publicinttribonacci(int n){int t[]={0,1,1};if(n <3)return t[n];for(int i =3; i <= n;++i){
t[i %3]= t[0]+ t[1]+ t[2];}return t[n %3];}}
classSolution{public:inttribonacci(int n){int t[]={0,1,1};if(n <3)return t[n];for(int i =3; i <= n;++i){
t[i %3]= t[0]+ t[1]+ t[2];}return t[n %3];}};
classSolution{/**
* @param {number} n
* @return {number}
*/tribonacci(n){const t =[0,1,1];if(n <3)return t[n];for(let i =3; i <= n;++i){
t[i %3]= t[0]+ t[1]+ t[2];}return t[n %3];}}
publicclassSolution{publicintTribonacci(int n){int[] t ={0,1,1};if(n <3)return t[n];for(int i =3; i <= n; i++){
t[i %3]= t[0]+ t[1]+ t[2];}return t[n %3];}}
functribonacci(n int)int{
t :=[]int{0,1,1}if n <3{return t[n]}for i :=3; i <= n; i++{
t[i%3]= t[0]+ t[1]+ t[2]}return t[n%3]}
class Solution {funtribonacci(n: Int): Int {val t =intArrayOf(0,1,1)if(n <3)return t[n]for(i in3..n){
t[i %3]= t[0]+ t[1]+ t[2]}return t[n %3]}}
classSolution{functribonacci(_ n:Int)->Int{var t =[0,1,1]if n <3{return t[n]}for i in3...n {
t[i %3]= t[0]+ t[1]+ t[2]}return t[n %3]}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Incorrect Base Cases
The tribonacci sequence has three base cases: T(0) = 0, T(1) = 1, and T(2) = 1. A common mistake is treating it like Fibonacci and only handling two base cases, or incorrectly setting T(0) = 1. Always explicitly check for n == 0, n == 1, and n == 2 and return the correct values before entering the main computation loop.
Integer Overflow in Recursive Solution
The naive recursive solution without memoization has exponential time complexity O(3^n), which causes timeout for even moderate values of n. Additionally, without memoization, the same subproblems are computed repeatedly, leading to massive redundant work. Always use memoization or an iterative approach to achieve linear time complexity.