There are two ways to implement a DP. They compute the same answers; they differ in direction.
Write the natural recursion and add a cache. You start from the big problem and recurse down to base cases, storing each result on the way back up.
memo = {}
function solve(n):
if base(n): return baseValue
if n in memo: return memo[n]
memo[n] = combine(solve(smaller(n)))
return memo[n]
Fill a table from the base cases upward, in an order where every value you need is already computed.
dp = new array of size n+1
dp[0], dp[1] = 0, 1
for i in 2..n:
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
They have the same time complexity. Reach for memoization when the recursion is obvious or the subproblem space is sparse; reach for tabulation when you want to avoid deep recursion or squeeze down memory. Most problems in this section can be solved either way — pick whichever you find clearer, but be able to do both.