Red-Green-Code

Deliberate practice techniques for software developers

  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter

Top-Down and Bottom-Up Dynamic Programming

By Duncan Smith Jan 24 0

An upside-down tree with the roots in the air and the leaves in the ground, as if it was growing in a mirror world. A robot is at the base of the tree, touching the trunk.

To solve a problem using dynamic programming, you have to break it into subproblems, solve those subproblems, and use the results to solve the original problem. So the first step in solving a dynamic programming problem is defining what subproblems you need to solve.

The standard way to define a dynamic programming problem and subproblems is by using a recursive function. As we saw with the Coin Change problem, we can calculate the target value $f(a)$ if we know the values $f(a-c_i)$ for each of the coin denominations $c_i$. And we can calculate each value $f(a-c_i)$ the same way, repeating this process until we get to one of the base cases: $f(0)=0$ or $f(x)=-1$.

Top-Down

Once you define a problem recursively using mathematical notation, it translates naturally into a recursive function in your target programming language. For Coin Change, the recursive function works as follows:

  • If the amount is a base case, return a constant.

  • Otherwise, for each coin denomination, make a recursive call.

  • Once all the recursive calls complete, return the final result.

You could implement a recursive solution to Coin Change exactly like this, but it would only be practical for small values. Dynamic programming comes to the rescue by adding one more idea: memoization. When you find an optimal result, save it in an array. Then, after checking the two base cases, check the array to see if you have already saved the answer. This way, you won’t make an exponential number of recursive calls and take an exponential amount of time to return the result.

A recursive dynamic programming implementation is called top-down because we start with the original problem (the top) and make recursive calls using smaller and smaller parameter values until we get to a base case (the bottom). The recursive call graph is often represented as a tree, with the root (the original problem) at the top and the leaves (the base cases) at the bottom. The child nodes of a node $A$ show which subproblems need to be solved to calculate the answer for the value of $A$.

Top-down is usually the easiest way to implement a dynamic programming solution because it follows directly from the mathematical definition of a subproblem.

Bottom-Up

The top-down approach starts with the largest input value, recursively calculates the answer for smaller and smaller inputs, and uses those results to find the answer to the original problem.

The alternative approach is to start with the base cases and calculate the answers for larger and larger inputs until you get to the target input. This approach is called bottom-up because it proceeds from the leaves of the tree upwards to the root.

A bottom-up dynamic programming solution uses iteration instead of recursion. For Coin Change, we can use two nested loops. One loop iterates through the coin denominations, and the other iterates through amounts. In the implementation shown in my article, the coin denominations are the outer loop. The inner loop iterates through each amount from the current coin amount to the target amount. At each step, we decide if it’s better to use the current coin to make the amount, or whether to stick with a previous result that used fewer coins.

Although memoization is often associated with the top-down approach, it may also be necessary for bottom-up implementations. For Coin Change, we need an array of size amount + 1, just as we did in the top-down implementation. For other dynamic programming problems, we may only need the last one or two values, in which case we don’t need a full memo table.

Top-down and bottom-up implementations generally have the same asymptotic time complexity. So if LeetCode accepts one implementation, it should also accept the other. If you measure the actual running times, a bottom-up implementation might be faster because it avoids the overhead of recursion and function calls. Or the top-down implementation might be faster if it avoids evaluating subproblems that aren’t necessary to solve the original problem. In performance-critical real-world programming, you would carry out performance tests to find out which is faster in practice. In an interview, you might be expected to know the trade-offs for the two approaches, in terms of code complexity, storage requirements, and runtime performance.

(Image credit: DALLĀ·E 3)

For an introduction to this year’s project, see A Project for 2024.

Categories: LeetCode

Prev
Next

Stay in the Know

I'm trying out the latest learning techniques on software development concepts, and writing about what works best. Sound interesting? Subscribe to my free newsletter to keep up to date. Learn More
Unsubscribing is easy, and I'll keep your email address private.

Getting Started

Are you new here? Check out my review posts for a tour of the archives:

  • 2023 in Review: 50 LeetCode Tips
  • 2022 in Review: Content Bots
  • 2021 in Review: Thoughts on Solving Programming Puzzles
  • Lessons from the 2020 LeetCode Monthly Challenges
  • 2019 in Review
  • Competitive Programming Frequently Asked Questions: 2018 In Review
  • What I Learned Working On Time Tortoise in 2017
  • 2016 in Review
  • 2015 in Review
  • 2015 Summer Review

Archives

Recent Posts

  • Do Coding Bots Mean the End of Coding Interviews? December 31, 2024
  • Another Project for 2024 May 8, 2024
  • Dynamic Programming Wrap-Up May 1, 2024
  • LeetCode 91: Decode Ways April 24, 2024
  • LeetCode 70: Climbing Stairs April 17, 2024
  • LeetCode 221: Maximal Square April 10, 2024
  • Using Dynamic Programming for Maximum Product Subarray April 3, 2024
  • LeetCode 62: Unique Paths March 27, 2024
  • LeetCode 416: Partition Equal Subset Sum March 20, 2024
  • LeetCode 1143: Longest Common Subsequence March 13, 2024
Red-Green-Code
  • Home
  • About
  • Contact
  • Project 462
  • CP FAQ
  • Newsletter
Copyright © 2025 Duncan Smith