Young Devs Bin
๐Ÿงฎ Algorithm

Breaking Down Stock Trading DP

ยท4 min readยท#leetcode#algorithm#dynamic-programming#greedy

Worked through three stock trading problems today. Interesting how the same base problem changes completely depending on the constraints.


121. Best Time to Buy and Sell Stock

LeetCode 121 โ†’

One buy, one sell. Find the max profit.

The key insight โ€” track the minimum price seen so far, and at each day check if selling today gives a better profit.

def maxProfit(prices):
    min_price = float('inf')
    max_profit = 0
 
    for price in prices:
        if price < min_price:
            min_price = price
        else:
            max_profit = max(max_profit, price - min_price)
 
    return max_profit

Can also think of it as Two Pointer โ€” left stays at the cheapest day, right moves forward. If right < left, move left to right.

Time: O(n) / Space: O(1)


122. Best Time to Buy and Sell Stock II

LeetCode 122 โ†’

Now you can buy and sell as many times as you want.

If you can trade every day, the answer is simple โ€” just take every upward move. That's Greedy.

def maxProfit(prices):
    max_profit = 0
 
    for i in range(1, len(prices)):
        if prices[i] > prices[i - 1]:
            max_profit += prices[i] - prices[i - 1]
 
    return max_profit

Time: O(n) / Space: O(1)

Same base problem as 121, but removing the "one trade only" constraint completely changes the approach โ€” from Two Pointer to Greedy.


309. Best Time to Buy and Sell Stock with Cooldown

LeetCode 309 โ†’

Multiple trades allowed, but after selling you have to wait one day before buying again.

Greedy doesn't work here because selling today affects what you can do the day after. So we need DP.

Method 1: Memoization (Top-down DP)

Think of it as a decision tree. At each day, you either buy, sell, or do nothing. The state is (index, can_buy).

def maxProfit(prices):
    dp = {}
 
    def dfs(i, availability):
        if i >= len(prices):
            return 0
        if (i, availability) in dp:
            return dp[(i, availability)]
 
        cooldown = dfs(i + 1, availability)
 
        if availability:
            buy = dfs(i + 1, False) - prices[i]
            dp[(i, availability)] = max(cooldown, buy)
        else:
            sell = dfs(i + 2, True) + prices[i]
            dp[(i, availability)] = max(sell, cooldown)
 
        return dp[(i, availability)]
 
    return dfs(0, True)

The cooldown is handled by jumping to i + 2 when selling โ€” skipping the next day entirely.

Why memoization works: The state (i, availability) has n ร— 2 possible combinations. Once computed, each state is cached in dp and never recalculated.

Time: O(n) / Space: O(n)


Method 2: Bottom-up DP (State Machine)

Instead of recursion, track three states at every day:

  • holding โ€” currently holding a stock
  • sold โ€” sold today
  • idle โ€” doing nothing (resting or in cooldown)
def maxProfit(prices):
    holding = -prices[0]
    sold = 0
    idle = 0
 
    for i in range(1, len(prices)):
        prev_holding = holding
        prev_sold = sold
        prev_idle = idle
 
        holding = max(prev_holding, prev_idle - prices[i])
        sold = prev_holding + prices[i]
        idle = max(prev_idle, prev_sold)
 
    return max(sold, idle)

State transitions:

  • holding โ†’ keep holding OR buy from idle
  • sold โ†’ sell from holding
  • idle โ†’ stay idle OR cooldown from sold (this is where cooldown is enforced)

The prev_ variables are necessary because all three states update simultaneously โ€” without them, an updated value would bleed into the next calculation in the same loop.

return max(sold, idle) โ€” holding is excluded because unrealized profit doesn't count. You have to actually sell to lock in the gain.

Time: O(n) / Space: O(1)


Which one to use in interviews?

Both are worth knowing. A good flow:

  1. Start with memoization โ€” the logic is clearer and easier to explain
  2. Then optimize to bottom-up DP โ€” better space complexity (O(n) โ†’ O(1))

Pattern

Same base problem, different constraints โ†’ different approach:

ProblemConstraintApproach
121One tradeTwo Pointer
122Unlimited tradesGreedy
309Cooldown after sellDP (memoization or state machine)