Stock Buy Sell Maximum Profit: Dynamic Programming Deep Dive
1. Introduction and Core Concepts
The stock buy sell maximum profit problem is one of the most iconic and frequently encountered problems in dynamic programming, algorithmic interviews, and quantitative finance. At its core, the problem asks a deceptively simple question: given an array where each element represents the price of a stock on a given day, what is the maximum profit you can achieve by buying and selling under a set of constraints? While the premise is rooted in financial markets, the underlying algorithmic patterns generalize to any scenario involving optimal timing of resource acquisition and disposal.
Understanding stock buy sell maximum profit is essential because it encapsulates fundamental dynamic programming principles such as optimal substructure, overlapping subproblems, and state transitions. Unlike naive brute-force approaches that try every possible pair of buy and sell days, the dynamic programming formulation allows us to build optimal solutions incrementally, making decisions based on the best outcomes of previous days. This efficiency is critical when dealing with large datasets, such as high-frequency trading data with millions of price points.
The problem family is not monolithic. It is actually a spectrum of variants defined by constraints: Are you allowed only a single transaction? Can you transact an unlimited number of times? Is there a limit of k transactions? Must you observe a cooldown period after selling? Is there a transaction fee for each trade? Each variant introduces new state dimensions and requires a refined recurrence relation. Mastering the base case of a single transaction provides the intuition needed to tackle the entire hierarchy, which is why this problem is a cornerstone for learning dynamic programming.
Why This Problem Matters Beyond Interviews
From an educational perspective, stock buy sell maximum profit serves as a perfect bridge between greedy algorithms and full dynamic programming. The single-transaction version can be solved greedily in linear time, but as soon as constraints like k transactions or cooldown are added, a greedy strategy fails and DP becomes necessary. This progression teaches when and why DP is required.
Practically, the same logic powers real-world optimization engines. The core idea of maximizing profit by choosing optimal intervals appears in resource allocation, job scheduling, cryptocurrency arbitrage, and even energy trading where you buy electricity when it is cheap and sell it back to the grid when prices peak. Learning this pattern equips you with a reusable template for any interval-based optimization problem.
2. How It Works
The dynamic programming approach to stock buy sell maximum profit works by defining a set of states that capture all relevant information at each day. The most common state definition is based on two dimensions: the current day index and whether you are currently holding a stock. For more complex variants, a third dimension is added to track the number of transactions completed or whether you are in a cooldown period. By defining these states precisely, we can express the optimal decision for today in terms of optimal decisions from yesterday.
For the foundational single-transaction problem, the logic is straightforward. We maintain the minimum price seen so far and the maximum profit achievable. As we iterate through prices, we have two choices each day: either we keep the previous minimum price, or we update it if today's price is lower. Similarly, we either keep the previous maximum profit or update it if selling today at the current price yields a higher profit. This is a form of DP with O(1) state, often called Kadane-like or greedy DP.
For the general case of at most k transactions, we define dp[i][k][hold] as the maximum profit achievable on day i with at most k transactions, where hold is 0 or 1 indicating whether we hold a stock. The recurrence relation becomes: dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) which means we either do nothing or we sell the stock we were holding. Conversely, dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) which means we either continue holding or we buy a new stock, which consumes one transaction. The base cases are dp[0][k][0] = 0, dp[0][k][1] = -prices[0], and dp[i][0][*] = 0.
State Machine Representation
A powerful way to visualize these recurrences is as a finite state machine. Each state represents a condition such as "holding", "not holding", or "in cooldown", and transitions represent actions like buy, sell, or rest. The profit associated with each transition is either -prices[i] for buying, +prices[i] for selling, or 0 for resting. The DP algorithm essentially finds the maximum weight path through this state machine across n days. This perspective makes it easy to extend the model: adding a cooldown simply adds a new state that you must pass through after selling before you can buy again.
Another key insight is that many variants can be optimized from a 3D DP table to 2D or even 1D arrays because each day's state only depends on the previous day's state. This space optimization does not change the underlying theory but is crucial for handling large inputs efficiently. Recognizing this dependency pattern is a hallmark of advanced DP design.
3. Implementation
Implementing stock buy sell maximum profit solutions in Python requires careful handling of state initialization and iteration order. Below are four canonical implementations that cover the majority of interview and practical scenarios. All code is written for clarity and includes detailed comments explaining the DP transitions. Note that all comparison operators are HTML-escaped for safe rendering.
Variant 1: Single Transaction (At Most One Buy and One Sell)
This is the most efficient variant and forms the basis for understanding all others. It runs in linear time with constant space by tracking the minimum price and maximum profit in a single pass.
def max_profit_single_transaction(prices):
"""
Maximum profit with at most one transaction.
Time: O(n), Space: O(1)
"""
if not prices or len(prices) < 2:
return 0
min_price = prices[0]
max_profit = 0
for price in prices[1:]:
# Update minimum price seen so far
if price < min_price:
min_price = price
# Check if selling today yields better profit
elif price - min_price > max_profit:
max_profit = price - min_price
return max_profit
# Example
prices = [7, 1, 5, 3, 6, 4]
print(max_profit_single_transaction(prices)) # Output: 5 (buy at 1, sell at 6)
Variant 2: Unlimited Transactions
When you can make as many transactions as you want, the optimal strategy is to capture every upward price movement. The DP collapses to a greedy sum of all positive adjacent differences.
def max_profit_unlimited_transactions(prices):
"""
Maximum profit with unlimited transactions.
Equivalent to summing all positive price differences.
Time: O(n), Space: O(1)
"""
if not prices or len(prices) < 2:
return 0
total_profit = 0
for i in range(1, len(prices)):
# If price increased from yesterday, capture the profit
if prices[i] > prices[i-1]:
total_profit += prices[i] - prices[i-1]
return total_profit
# Alternative DP state machine view
def max_profit_unlimited_dp(prices):
if not prices:
return 0
hold = -prices[0] # Max profit when holding a stock
cash = 0 # Max profit when not holding
for price in prices[1:]:
prev_hold = hold
prev_cash = cash
# Either keep holding or buy today
hold = max(prev_hold, prev_cash - price)
# Either keep cash or sell today
cash = max(prev_cash, prev_hold + price)
return cash
Variant 3: At Most K Transactions (General DP)
This is the most general and powerful formulation. It uses a 2D DP table where dp[k][0] represents max profit not holding and dp[k][1] represents max profit holding after k transactions.
def max_profit_k_transactions(prices, k):
"""
Maximum profit with at most k transactions.
Time: O(n * k), Space: O(k)
Uses optimized 1D DP arrays.
"""
if not prices or k == 0:
return 0
n = len(prices)
# If k is large enough, it becomes unlimited transactions
if k >= n // 2:
return max_profit_unlimited_transactions(prices)
# dp_hold[j] = max profit holding a stock with j transactions
# dp_cash[j] = max profit not holding with j transactions
dp_hold = [-float('inf')] * (k + 1)
dp_cash = [0] * (k + 1)
for price in prices:
for j in range(1, k + 1):
# Update hold state: either keep holding or buy today (using j-1 cash)
# Must iterate j backwards? No, because we use previous day's values
# So we need to store previous iteration values
pass
# Correct iterative implementation with proper ordering
hold = [-prices[0]] * (k + 1)
cash = [0] * (k + 1)
for i in range(1, n):
price = prices[i]
for j in range(1, k + 1):
# Use previous day's values, so copy first
prev_hold = hold[j]
prev_cash = cash[j]
# For hold, we need cash[j-1] from previous day
# To avoid overwrite issues, iterate j from k down to 1 when updating in place
# Here we use separate logic with temp variables for clarity
hold[j] = max(hold[j], cash[j-1] - price)
cash[j] = max(cash[j], prev_hold + price)
return cash[k]
# Example: k=2, prices=[3,2,6,5,0,3] -> 7 (buy 2 sell 6, buy 0 sell 3)
print(max_profit_k_transactions([3, 2, 6, 5, 0, 3], 2))
Variant 4: With Cooldown and Transaction Fee
Real markets have frictions. A cooldown means you cannot buy the day after you sell, and a fee is charged on each transaction. Both require an extra state dimension.
def max_profit_with_cooldown(prices):
"""
Maximum profit with cooldown (must wait one day after selling before buying).
States: hold, sold, rest
Time: O(n), Space: O(1)
"""
if not prices or len(prices) < 2:
return 0
hold = -prices[0] # Holding a stock
sold = 0 # Just sold today (in cooldown tomorrow)
rest = 0 # Not holding and not in cooldown (can buy)
for price in prices[1:]:
prev_hold = hold
prev_sold = sold
prev_rest = rest
# Hold: either keep holding or buy from rest state (cannot buy from sold)
hold = max(prev_hold, prev_rest - price)
# Sold: sell the stock we were holding
sold = prev_hold + price
# Rest: either stay in rest or come from sold (cooldown over)
rest = max(prev_rest, prev_sold)
# Final profit must be in sold or rest (not holding)
return max(sold, rest)
def max_profit_with_fee(prices, fee):
"""
Maximum profit with transaction fee charged on each sell.
Time: O(n), Space: O(1)
"""
if not prices:
return 0
hold = -prices[0]
cash = 0
for price in prices[1:]:
prev_hold = hold
prev_cash = cash
hold = max(prev_hold, prev_cash - price)
# Pay fee when selling
cash = max(prev_cash, prev_hold + price - fee)
return cash
print(max_profit_with_cooldown([1, 2, 3, 0, 2])) # Output: 3
print(max_profit_with_fee([1, 3, 2, 8, 4, 9], 2)) # Output: 8
4. Step-by-Step Example
Let us walk through a concrete example for the single-transaction problem to solidify the algorithm's behavior. Consider prices = [7, 1, 5, 3, 6, 4]. The goal is to find the maximum profit from one buy and one later sell. The optimal answer is 5, achieved by buying at 1 and selling at 6.
We initialize min_price = 7 and max_profit = 0. On day 0, price is 7. This becomes our initial minimum. On day 1, price is 1. Since 1 < 7, we update min_price to 1. No profit is calculated yet because we have not had a chance to sell after this new low. The max_profit remains 0.
On day 2, price is 5. Now we calculate potential profit if we sell today: 5 - 1 = 4. Since 4 > 0, we update max_profit to 4. The min_price stays at 1. On day 3, price is 3. Potential profit is 3 - 1 = 2, which is not greater than 4, so max_profit remains 4. On day 4, price is 6. Potential profit is 6 - 1 = 5, which is greater than 4, so max_profit becomes 5. On day 5, price is 4, potential profit is 3, which does not beat 5. The final answer is 5.
Walkthrough for K=2 Transactions
Now consider a more complex example with at most 2 transactions: prices = [3, 2, 6, 5, 0, 3]. A greedy single-transaction approach would only capture profit 4 (buy at 2 sell at 6) or 3 (buy at 0 sell at 3), but the optimal with two transactions is 7 by doing both. Let us trace the DP table for k=2. We maintain arrays hold[0..2] and cash[0..2]. Initialization: hold = [-3, -3, -3], cash = [0, 0, 0].
Day 1 price=2: For j=1, hold[1] = max(-3, 0-2) = -2, cash[1] = max(0, -3+2) = 0. For j=2, hold[2] = max(-3, 0-2) = -2, cash[2] = max(0, -3+2) = 0. Day 2 price=6: hold[1] = max(-2, 0-6) = -2, cash[1] = max(0, -2+6) = 4. hold[2] = max(-2, 4-6) = -2, cash[2] = max(0, -2+6) = 4. The profit 4 is now recorded in both cash[1] and cash[2]. Day 4 price=0: hold[1] = max(-2, 0-0) = 0, cash[1] stays 4. hold[2] = max(-2, 4-0) = 4, cash[2] stays 4. Notice how hold[2] becomes 4, representing buying at 0 after having already secured profit 4 from the first transaction. Day 5 price=3: cash[2] = max(4, 4+3) = 7, which is the final answer. This trace shows how the DP correctly chains transactions.
This step-by-step process reveals why backward iteration or temporary variables are needed in the 1D optimization: if we updated hold[1] and then immediately used the new cash[1] to update hold[2] in the same day, we would incorrectly allow two transactions on the same day. Proper implementation ensures each day's transitions use the previous day's values.
5. Complexity Analysis
Complexity analysis for stock buy sell maximum profit varies dramatically by variant. The choice of algorithm determines whether a solution will scale to millions of data points or fail with time limit exceeded errors. Understanding these trade-offs is essential for selecting the right approach in production systems and interviews.
The single-transaction and unlimited-transaction variants are the most efficient, both achieving O(n) time and O(1) space. They require only a single linear scan and a few scalar variables. This makes them suitable for real-time streaming data where prices arrive continuously and you need to maintain the best profit seen so far with minimal memory overhead.
The general k-transaction variant is more expensive. The naive 3D DP implementation uses O(n * k) time and O(n * k) space, where n is the number of days and k is the transaction limit. This can be optimized to O(k) space by observing that we only need the previous day's values, but the time complexity remains O(n * k). When k is large (k >= n/2), this degrades to the unlimited case and we can short-circuit to the O(n) greedy solution, which is a critical optimization often missed in interviews.
Variants with cooldown or transaction fees add constant factor overhead but remain O(n) time and O(1) space, as they only require maintaining three states instead of two. The divide-and-conquer approach, while theoretically interesting, is rarely used in practice because it has O(n log n) time and O(log n) space due to recursion, and is more complex to implement correctly.
| Variant / Operation | Best Case | Average Case | Worst Case | Space Complexity |
|---|---|---|---|---|
| Single Transaction (One Buy/Sell) | O(n) | O(n) | O(n) | O(1) |
| Unlimited Transactions | O(n) | O(n) | O(n) | O(1) |
| At Most K Transactions (DP) | O(n * k) | O(n * k) | O(n * k) | O(k) optimized, O(n*k) naive |
| With Cooldown (1-day) | O(n) | O(n) | O(n) | O(1) |
| With Transaction Fee | O(n) | O(n) | O(n) | O(1) |
| Brute Force (All Pairs) | O(n^2) | O(n^2) | O(n^2) | O(1) |
| Divide and Conquer | O(n log n) | O(n log n) | O(n log n) | O(log n) |
The table above highlights a critical insight: best, average, and worst cases are identical for these algorithms because they must always scan the entire price array. There is no early exit condition that depends on input distribution. Space complexity is where the most meaningful optimization occurs, reducing from O(n*k) to O(k) by using rolling arrays. In memory-constrained environments like embedded trading systems, this optimization is mandatory.
6. Variations and Optimizations
The stock buy sell maximum profit problem has spawned numerous variations that model increasingly realistic market conditions. Each variation introduces a new constraint that requires a modification to the state definition or transition logic, providing excellent practice for general DP modeling.
One common optimization is the handling of large k. As noted earlier, if k is greater than or equal to n/2, you can perform a transaction every other day, which is effectively unlimited. Checking for this condition at the start of the k-transaction function and delegating to the O(n) unlimited solution prevents an O(n^2) blowup when k is large. This is a classic example of exploiting problem structure to avoid unnecessary computation.
Another important variation is the inclusion of a transaction fee. In this model, every sell incurs a fixed cost, which discourages frequent trading. The DP modification is subtle: you subtract the fee only when transitioning from hold to cash (selling), not when buying. Some formulations charge the fee on buying instead, but the result is equivalent. The key is to be consistent and not double-count the fee.
The cooldown variation, popularized by LeetCode 309, requires a one-day waiting period after selling before you can buy again. This is modeled with three states: hold, sold, and rest. The sold state represents the day you just sold, and you cannot transition from sold to hold directly; you must go through rest first. This prevents consecutive buy-sell-buy patterns that would violate the cooldown rule.
Further advanced variations include: at most two transactions (a special case of k=2 that can be solved with four variables in O(n) time without general DP), with at most one stock held at a time (the default), and where you can hold multiple stocks simultaneously (rare, but changes the recurrence). There is also the variant where you must sell before you can buy again, which is the standard assumption, versus allowing short selling.
| Variant | Allowed Transactions | DP State Dimensions | Key Transition Modification | Typical Use Case |
|---|---|---|---|---|
| Single Transaction | 1 | 1D (min_price) | Track min so far | One-time investment, interview warm-up |
| Unlimited | Infinite | 2 states (hold/cash) | Sum positive diffs | High-frequency trading without fees |
| At Most K | k | 3D (day, k, hold) | Consume one k on buy | Portfolio with limited trades per quarter |
| Exactly K | Exactly k | 3D with careful init | Initialize with -inf except 0 | Contractual obligation to trade k times |
| With Cooldown | Unlimited + cooldown | 3 states (hold/sold/rest) | Cannot buy from sold | Regulatory settlement period (T+1) |
| With Fee | Unlimited + fee | 2 states + fee | Subtract fee on sell | Brokerage commission modeling |
| At Most 2 Transactions | 2 | 4 variables | Two buy/sell pairs | Common interview follow-up |
Space optimization is another critical area. The naive DP for k transactions uses a table dp[n][k+1][2], which for n=10^5 and k=100 would require 20 million integers. By using two arrays of size k+1 and updating them iteratively, we reduce space to O(k). For the cooldown variant, we only need three variables regardless of n. These optimizations are not just theoretical; they determine whether a solution passes memory limits on platforms like LeetCode or in production systems processing years of tick data.
7. Real-World Applications
While the problem is framed in terms of stocks, the underlying pattern of optimal interval selection with state constraints appears in a surprising number of real-world domains far beyond finance. Recognizing this abstraction allows engineers to apply the same DP template to diverse optimization challenges.
In financial technology and algorithmic trading, these DP models are directly deployed. A hedge fund might use the k-transaction model to backtest a strategy that is limited to k trades per month due to risk management policies. The transaction fee variant accurately models brokerage fees and slippage, while the cooldown variant models settlement periods where funds from a sale are not immediately available for reinvestment. Even cryptocurrency arbitrage bots use the unlimited transaction logic to decide when to execute triangular arbitrage across exchanges, capturing every profitable price discrepancy.
In cloud computing and resource management, the same logic applies to buying and selling computational resources. For example, AWS spot instances have fluctuating prices. An autoscaling system can be modeled as buying instances when prices are low and releasing them when prices are high, maximizing cost savings while maintaining capacity. The cooldown constraint models the time needed to spin up or tear down instances, during which no new purchases can be made.
In energy systems, particularly with the rise of smart grids and battery storage, the problem becomes physical. A homeowner with a Tesla Powerwall or a grid-scale battery facility buys electricity from the grid when prices are low (often at night) and sells it back or uses it to avoid buying when prices peak during the day. The DP with transaction fees models battery degradation cost per charge cycle, and the cooldown models the physical limitation that a battery cannot charge and discharge simultaneously.
Other applications include database query optimization where you decide when to materialize views, network bandwidth trading, supply chain management where you buy commodities in bulk when cheap and sell products when demand is high, and even job scheduling where each job has a start time, end time, and profit, and you want to select non-overlapping jobs to maximize profit. The core DP state machine of hold versus not-hold translates directly to busy versus idle in scheduling problems.
From Theory to Production
In production, these algorithms are rarely used in isolation. They are components of larger pipelines that include price forecasting, risk assessment, and transaction execution. For instance, the DP might be run on predicted future prices from a machine learning model rather than historical prices, turning it into a decision engine for future actions. The efficiency of the O(n) and O(n*k) solutions is crucial here, as they may be executed thousands of times per second across different assets and time horizons.
8. Comparison with Alternatives
Choosing the right algorithmic approach for stock buy sell maximum profit depends on constraints, input size, and the need for optimality guarantees. While dynamic programming is the most versatile, other paradigms like brute force, greedy, and divide and conquer each have their place and trade-offs.
Brute force is the most intuitive approach: try every possible pair of buy and sell days for single transaction, or every possible combination of transactions for multiple trades. It is easy to implement and guarantees correctness, but its O(n^2) or O(n^k) time complexity makes it infeasible for n greater than a few thousand. It is primarily useful as a reference implementation for testing the correctness of optimized solutions on small inputs.
The greedy approach works perfectly for the unlimited transactions variant, where picking every profitable adjacent pair is optimal. It is also optimal for the single transaction variant when implemented as the min-price scan. However, greedy fails for the k-transaction and cooldown variants because locally optimal choices do not lead to globally optimal solutions. For example, taking a small profit early might prevent a larger profit later when transaction count is limited.
Divide and conquer, similar to the maximum subarray problem, can solve the single transaction case in O(n log n) by finding the maximum profit in the left half, right half, and crossing the midpoint. While it achieves better than brute force, it is still slower than the O(n) DP/greedy solution and is more complex to implement. Its main value is educational, demonstrating that the problem has the same structure as maximum subarray.
| Feature | Brute Force | Greedy | Dynamic Programming | Divide and Conquer |
|---|---|---|---|---|
| Time Complexity (Single Tx) | O(n^2) | O(n) | O(n) | O(n log n) |
| Time Complexity (K Tx) | O(n^k) exponential | Not optimal | O(n * k) | Not applicable |
| Space Complexity | O(1) | O(1) | O(k) optimized | O(log n) recursion |
| Optimality Guarantee | Yes | Only for unlimited / single | Yes for all variants | Yes for single only |
| Handles Cooldown / Fee | Yes (with modifications) | No | Yes (add state) | No |
| Implementation Complexity | Very Low | Low | Medium | Medium-High |
| Scalability (n=10^6) | Infeasible | Excellent | Excellent (if k small) | Good |
| Best Use Case | Testing / small n | Unlimited transactions | General / constrained variants | Educational / alternative view |
The table makes it clear that dynamic programming is the only approach that maintains optimality across all variants while remaining computationally feasible. The greedy method is a specialized, faster subset of DP that works when the state machine has no memory of past transactions beyond the current hold status. Understanding when greedy suffices and when full DP is required is a key skill in algorithm design.
9. Common Mistakes and Best Practices
Even experienced developers make subtle errors when implementing stock buy sell maximum profit solutions. These mistakes often stem from off-by-one errors, incorrect state initialization, or misunderstanding the problem constraints.
One of the most frequent mistakes is allowing a buy and sell on the same day to count as a transaction, or allowing multiple transactions to occur simultaneously. The DP recurrence must ensure that a buy consumes a transaction and that you cannot buy if you are already holding. Initializing hold to -infinity except for hold[0] = -prices[0] prevents invalid states where you hold without having bought.
Another common pitfall is integer overflow or incorrect handling of negative profits. The problem typically requires returning 0 if no profit is possible, rather than a negative value. Initializing max_profit to 0 and using max() correctly ensures this. For the k-transaction DP, initializing cash arrays to 0 and hold arrays to negative infinity is crucial; initializing hold to 0 would incorrectly allow free acquisition of stock.
A third mistake is failing to handle the large k optimization. Without the check if k >= n // 2, the O(n*k) solution will time out when k is 10^9, a common test case designed to catch this oversight. Always include this early return to the unlimited transaction logic.
Forgetting to escape the cooldown constraint is also typical. Developers often implement the cooldown DP but allow a transition from sold directly to hold, which violates the one-day waiting period. The correct transition is hold can only come from rest, not from sold. Drawing the state machine diagram before coding helps prevent this.
Best Practices
- Clarify Constraints First: Before coding, ask whether you can hold multiple stocks, whether you must sell before buying again, and whether k is at most or exactly k. These details change the DP initialization.
- Use State Machine Diagrams: Visualize hold, cash, sold, and rest states and their transitions. This makes the recurrence relations obvious and reduces bugs.
- Optimize Space Deliberately: Start with a clear 2D or 3D table for correctness, then optimize to rolling arrays. Do not try to write the optimized version from scratch, as it is error-prone.
- Test Edge Cases: Always test with empty array, single element, strictly decreasing prices (profit 0), strictly increasing prices, and k=0. These catch initialization errors.
- Use Temporary Variables: When optimizing to O(k) space, store previous day's values in temporary variables or iterate k backwards to avoid overwriting data needed for the current day's calculations.
- Separate Fee Logic: Decide upfront whether fee is charged on buy or sell and be consistent. Charging on sell is more intuitive as it models brokerage commission.
Following these practices ensures your solution is not only correct but also robust, readable, and ready for production or interview scrutiny. Writing helper functions for the unlimited case and reusing them in the k-transaction solution also promotes DRY principles and reduces duplication.
10. Interview Questions
Stock buy sell maximum profit is a staple of technical interviews at top technology companies. Interviewers use it to assess understanding of dynamic programming, state modeling, and optimization. Below are five common questions, ranging from warm-up to advanced, with guidance on what interviewers are looking for.
Question 1: Single Transaction - The Warm-Up
Given an array prices where prices[i] is the price on day i, find the maximum profit you can achieve with at most one transaction. If no profit is possible, return 0. This is the entry point. The expected answer is the O(n) single-pass solution tracking min_price. Interviewers look for you to avoid the O(n^2) brute force and to handle edge cases like empty input. A follow-up is often to return the actual buy and sell days, not just the profit, which requires tracking indices alongside min_price.
Question 2: Unlimited Transactions
What if you can make as many transactions as you want, but you can only hold one share at a time? The optimal solution is to sum all positive adjacent differences. Interviewers want to see if you recognize the greedy property and can explain why it is optimal: any increasing sequence can be decomposed into adjacent steps without losing profit. They may also ask you to prove this or to implement the DP state machine version with hold and cash variables.
Question 3: At Most Two Transactions
Design an algorithm to find maximum profit with at most two transactions. This is a classic follow-up that tests whether you can extend the general k-transaction DP or find a more optimized O(n) solution with four variables (first buy, first sell, second buy, second sell). The four-variable solution is: first_buy = max(first_buy, -price), first_sell = max(first_sell, first_buy + price), second_buy = max(second_buy, first_sell - price), second_sell = max(second_sell, second_buy + price). Interviewers look for clean variable management and understanding of transaction chaining.
Question 4: With Cooldown
After you sell, you cannot buy the next day (cooldown of one day). Find maximum profit. This tests state machine modeling. The expected solution uses three states: hold, sold, rest. Interviewers evaluate your ability to define states correctly and write the transitions: hold = max(hold, rest - price), sold = hold + price, rest = max(rest, sold). They often ask you to walk through an example like [1,2,3,0,2] and explain why you cannot buy on day 3 after selling on day 2.
Question 5: With Transaction Fee and Large K
You are given k and a transaction fee. How would you handle k up to 10^9 efficiently? This combines two advanced concepts. The fee part tests if you know to subtract fee on sell: cash = max(cash, hold + price - fee). The large k part tests the optimization: if k >= n//2, delegate to the unlimited solution. Interviewers use this to see if you consider constraints and can optimize for extreme inputs. A strong candidate will mention both time and space complexity and discuss the trade-off between the O(n*k) DP and the O(n) shortcut, and will write code that handles both fee and large k correctly.
Preparing for these questions involves not just memorizing code, but understanding the underlying state transitions so you can derive the solution on the fly for any new constraint the interviewer invents, such as a two-day cooldown or a limit on holding period.
Frequently Asked Questions
What is Stock Buy Sell Maximum Profit and how does it work?
Stock Buy Sell Maximum Profit is a fundamental concept in computer science. It involves specific techniques and data organization methods designed to solve computational problems efficiently. Understanding the core mechanics helps developers choose the right approach for their applications.
What is the time complexity of Stock Buy Sell Maximum Profit?
The time complexity of Stock Buy Sell Maximum Profit varies depending on the specific operation and implementation. Common complexities range from O(1) constant time for basic operations to O(n log n) or O(n²) for more complex operations. The article above provides detailed complexity analysis for each operation.
When should I use Stock Buy Sell Maximum Profit in my projects?
Use Stock Buy Sell Maximum Profit when your application requires the specific performance characteristics it provides. Consider factors like data size, operation frequency, memory constraints, and whether the problem domain matches the strengths of Stock Buy Sell Maximum Profit. The real-world applications section above covers common use cases.
What are the advantages and disadvantages of Stock Buy Sell Maximum Profit?
The main advantages of Stock Buy Sell Maximum Profit include efficient performance for its target use cases and well-understood theoretical guarantees. Disadvantages may include implementation complexity, memory overhead, or performance limitations in certain edge cases. Always benchmark against alternatives for your specific workload.
How does Stock Buy Sell Maximum Profit compare to its alternatives?
Stock Buy Sell Maximum Profit offers distinct trade-offs compared to alternative approaches. While it excels in certain scenarios, other algorithms or data structures may be better suited for different problem constraints. The comparison table in the article provides a detailed analysis of these trade-offs.